Commit b164af65d19f0cd78afd2886e112129867e394a9

Authored by Marcos Pereira
Committed by Rodrigo Souto
1 parent 13fd87d5

API improvements:

- Add several endpoints
- Add some API documentation
- Fix plugins API hotspot

Signed-off-by: Victor Costa <vfcosta@gmail.com>
Signed-off-by: Leandro Nunes dos Santos <leandronunes@gmail.com>
Signed-off-by: Evandro Junior <evandrojr@gmail.com>
Signed-off-by: Rodrigo Souto <rodrigo@colivre.coop.br>
Signed-off-by: Caio SBA <caio@colivre.coop.br>
Signed-off-by: Carlos Purificacao <carloseugenio@gmail.com>
Signed-off-by: Ábner Silva de Oliveira <abner.oliveira@serpro.gov.br>
Signed-off-by: Aurelio A. Heckert <aurelio@colivre.coop.br>
Signed-off-by: Evandro Magalhaes Leite Junior <evandro.leite@serpro.gov.br>
Signed-off-by: Marcelo Júnior <maljunior@gmail.com>
Signed-off-by: Michel Felipe de Oliveira Ferreira <michel.ferreira@serpro.gov.br>
Signed-off-by: Larissa Reis <larissa@colivre.coop.br>
Signed-off-by: ABNER SILVA DE OLIVEIRA <abner.oliveira@serpro.gov.br>
Signed-off-by: Luciano Prestes Cavalcanti <lucianopcbr@gmail.com>
Signed-off-by: Marcos Ronaldo <marcos.rpj2@gmail.com>
Signed-off-by: Simião Carvalho <simiaosimis@gmail.com>
Signed-off-by: Gabriel Silva <gabriel93.silva@gmail.com>
app/controllers/application_controller.rb
@@ -184,13 +184,7 @@ class ApplicationController &lt; ActionController::Base @@ -184,13 +184,7 @@ class ApplicationController &lt; ActionController::Base
184 end 184 end
185 185
186 include SearchTermHelper 186 include SearchTermHelper
187 -  
188 - def find_by_contents(asset, context, scope, query, paginate_options={:page => 1}, options={})  
189 - scope = scope.with_templates(options[:template_id]) unless options[:template_id].blank?  
190 - search = plugins.dispatch_first(:find_by_contents, asset, scope, query, paginate_options, options)  
191 - register_search_term(query, scope.count, search[:results].count, context, asset)  
192 - search  
193 - end 187 + include FindByContents
194 188
195 def find_suggestions(query, context, asset, options={}) 189 def find_suggestions(query, context, asset, options={})
196 plugins.dispatch_first(:find_suggestions, query, context, asset, options) 190 plugins.dispatch_first(:find_suggestions, query, context, asset, options)
lib/find_by_contents.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +module FindByContents
  2 +
  3 + def find_by_contents(asset, context, scope, query, paginate_options={:page => 1}, options={})
  4 + scope = scope.with_templates(options[:template_id]) unless options[:template_id].blank?
  5 + search = plugins.dispatch_first(:find_by_contents, asset, scope, query, paginate_options, options)
  6 + register_search_term(query, scope.count, search[:results].count, context, asset)
  7 + search
  8 + end
  9 +
  10 +end
  11 +
lib/noosfero/api/api.rb
1 require 'grape' 1 require 'grape'
2 #require 'rack/contrib' 2 #require 'rack/contrib'
3 -  
4 Dir["#{Rails.root}/lib/noosfero/api/*.rb"].each {|file| require file unless file =~ /api\.rb/} 3 Dir["#{Rails.root}/lib/noosfero/api/*.rb"].each {|file| require file unless file =~ /api\.rb/}
  4 +
5 module Noosfero 5 module Noosfero
6 module API 6 module API
7 class API < Grape::API 7 class API < Grape::API
@@ -17,7 +17,6 @@ module Noosfero @@ -17,7 +17,6 @@ module Noosfero
17 end 17 end
18 18
19 @@NOOSFERO_CONF = nil 19 @@NOOSFERO_CONF = nil
20 -  
21 def self.NOOSFERO_CONF 20 def self.NOOSFERO_CONF
22 if @@NOOSFERO_CONF 21 if @@NOOSFERO_CONF
23 @@NOOSFERO_CONF 22 @@NOOSFERO_CONF
@@ -27,9 +26,11 @@ module Noosfero @@ -27,9 +26,11 @@ module Noosfero
27 end 26 end
28 end 27 end
29 28
  29 + before { set_locale }
30 before { setup_multitenancy } 30 before { setup_multitenancy }
31 before { detect_stuff_by_domain } 31 before { detect_stuff_by_domain }
32 before { filter_disabled_plugins_endpoints } 32 before { filter_disabled_plugins_endpoints }
  33 + before { init_noosfero_plugins }
33 after { set_session_cookie } 34 after { set_session_cookie }
34 35
35 version 'v1' 36 version 'v1'
@@ -41,11 +42,17 @@ module Noosfero @@ -41,11 +42,17 @@ module Noosfero
41 42
42 mount V1::Articles 43 mount V1::Articles
43 mount V1::Comments 44 mount V1::Comments
  45 + mount V1::Users
44 mount V1::Communities 46 mount V1::Communities
45 mount V1::People 47 mount V1::People
46 mount V1::Enterprises 48 mount V1::Enterprises
47 mount V1::Categories 49 mount V1::Categories
48 mount V1::Tasks 50 mount V1::Tasks
  51 + mount V1::Tags
  52 + mount V1::Environments
  53 + mount V1::Search
  54 + mount V1::Contacts
  55 +
49 mount Session 56 mount Session
50 57
51 # hook point which allow plugins to add Grape::API extensions to API::API 58 # hook point which allow plugins to add Grape::API extensions to API::API
lib/noosfero/api/entities.rb
@@ -6,6 +6,17 @@ module Noosfero @@ -6,6 +6,17 @@ module Noosfero
6 date.strftime('%Y/%m/%d %H:%M:%S') if date 6 date.strftime('%Y/%m/%d %H:%M:%S') if date
7 end 7 end
8 8
  9 + def self.can_display? profile, options, field, admin_only = false
  10 + current = options[:current_person]
  11 + admin = !current.blank? && current.is_admin?
  12 + owner = !current.blank? && current == profile
  13 + public_field = profile.public_fields.include? field.to_s
  14 + friend = !current.blank? && current.friends.include?(profile)
  15 +
  16 + return true if admin
  17 + return !admin_only && (owner||friend||public_field)
  18 + end
  19 +
9 class Image < Entity 20 class Image < Entity
10 root 'images', 'image' 21 root 'images', 'image'
11 22
@@ -30,66 +41,81 @@ module Noosfero @@ -30,66 +41,81 @@ module Noosfero
30 end 41 end
31 end 42 end
32 43
  44 + class CategoryBase < Entity
  45 + root 'categories', 'category'
  46 + expose :name, :id, :slug
  47 + end
  48 +
  49 + class Category < CategoryBase
  50 + root 'categories', 'category'
  51 + expose :full_name do |category, options|
  52 + category.full_name
  53 + end
  54 + expose :parent, :using => CategoryBase, if: { parent: true }
  55 + expose :children, :using => CategoryBase, if: { children: true }
  56 + expose :image, :using => Image
  57 + expose :display_color
  58 + end
  59 +
  60 + class Region < Category
  61 + root 'regions', 'region'
  62 + expose :parent_id
  63 + end
  64 +
33 class Profile < Entity 65 class Profile < Entity
34 expose :identifier, :name, :id 66 expose :identifier, :name, :id
35 expose :created_at, :format_with => :timestamp 67 expose :created_at, :format_with => :timestamp
36 expose :updated_at, :format_with => :timestamp 68 expose :updated_at, :format_with => :timestamp
37 expose :image, :using => Image 69 expose :image, :using => Image
  70 + expose :region, :using => Region
38 end 71 end
39 72
40 - class User < Entity 73 + class UserBasic < Entity
41 expose :id 74 expose :id
42 expose :login 75 expose :login
43 end 76 end
44 77
45 class Person < Profile 78 class Person < Profile
46 root 'people', 'person' 79 root 'people', 'person'
47 - expose :user, :using => User 80 + expose :user, :using => UserBasic, documentation: {type: 'User', desc: 'The user data of a person' }
48 end 81 end
  82 +
49 class Enterprise < Profile 83 class Enterprise < Profile
50 root 'enterprises', 'enterprise' 84 root 'enterprises', 'enterprise'
51 end 85 end
  86 +
52 class Community < Profile 87 class Community < Profile
53 root 'communities', 'community' 88 root 'communities', 'community'
54 expose :description 89 expose :description
55 - expose :categories  
56 - expose :members, :using => Person  
57 - end  
58 -  
59 - class CategoryBase < Entity  
60 - root 'categories', 'category'  
61 - expose :name, :id  
62 - end  
63 -  
64 - class Category < CategoryBase  
65 - root 'categories', 'category'  
66 - expose :slug  
67 - expose :full_name do |category, options|  
68 - category.full_name 90 + expose :admins do |community, options|
  91 + community.admins.map{|admin| {"name"=>admin.name, "id"=>admin.id}}
69 end 92 end
70 - expose :parent, :using => CategoryBase, if: { parent: true }  
71 - expose :children, :using => CategoryBase, if: { children: true }  
72 - expose :image, :using => Image 93 + expose :categories, :using => Category
  94 + expose :members, :using => Person
73 end 95 end
74 96
75 class ArticleBase < Entity 97 class ArticleBase < Entity
76 root 'articles', 'article' 98 root 'articles', 'article'
77 expose :id 99 expose :id
78 expose :body 100 expose :body
79 - expose :abstract 101 + expose :abstract, documentation: {type: 'String', desc: 'Teaser of the body'}
80 expose :created_at, :format_with => :timestamp 102 expose :created_at, :format_with => :timestamp
81 expose :updated_at, :format_with => :timestamp 103 expose :updated_at, :format_with => :timestamp
82 expose :title, :documentation => {:type => "String", :desc => "Title of the article"} 104 expose :title, :documentation => {:type => "String", :desc => "Title of the article"}
83 - expose :created_by, :as => :author, :using => Profile  
84 - expose :profile, :using => Profile 105 + expose :created_by, :as => :author, :using => Profile, :documentation => {type: 'Profile', desc: 'The profile author that create the article'}
  106 + expose :profile, :using => Profile, :documentation => {type: 'Profile', desc: 'The profile associated with the article'}
85 expose :categories, :using => Category 107 expose :categories, :using => Category
86 expose :image, :using => Image 108 expose :image, :using => Image
87 - #TODO Apply vote stuff in core and make this test  
88 expose :votes_for 109 expose :votes_for
89 expose :votes_against 110 expose :votes_against
90 expose :setting 111 expose :setting
91 expose :position 112 expose :position
92 expose :hits 113 expose :hits
  114 + expose :start_date
  115 + expose :end_date, :documentation => {type: 'DateTime', desc: 'The date of finish of the article'}
  116 + expose :tag_list
  117 + expose :children_count
  118 + expose :slug, :documentation => {:type => "String", :desc => "Trimmed and parsed name of a article"}
93 expose :path 119 expose :path
94 end 120 end
95 121
@@ -106,8 +132,31 @@ module Noosfero @@ -106,8 +132,31 @@ module Noosfero
106 expose :author, :using => Profile 132 expose :author, :using => Profile
107 end 133 end
108 134
  135 + class User < Entity
  136 + root 'users', 'user'
  137 +
  138 + attrs = [:id,:login,:email,:activated?]
  139 + aliases = {:activated? => :activated}
  140 +
  141 + attrs.each do |attribute|
  142 + name = aliases.has_key?(attribute) ? aliases[attribute] : attribute
  143 + expose attribute, :as => name, :if => lambda{|user,options| Entities.can_display?(user.person, options, attribute)}
  144 + end
  145 +
  146 + expose :person, :using => Person
  147 + expose :permissions, :if => lambda{|user,options| Entities.can_display?(user.person, options, :permissions, true)} do |user, options|
  148 + output = {}
  149 + user.person.role_assignments.map do |role_assigment|
  150 + if role_assigment.resource.respond_to?(:identifier) && !role_assigment.role.nil?
  151 + output[role_assigment.resource.identifier] = role_assigment.role.permissions
  152 + end
  153 + end
  154 + output
  155 + end
  156 + end
  157 +
109 class UserLogin < User 158 class UserLogin < User
110 - expose :private_token 159 + expose :private_token, documentation: {type: 'String', desc: 'A valid authentication code for post/delete api actions'}
111 end 160 end
112 161
113 class Task < Entity 162 class Task < Entity
@@ -116,6 +165,16 @@ module Noosfero @@ -116,6 +165,16 @@ module Noosfero
116 expose :type 165 expose :type
117 end 166 end
118 167
  168 + class Environment < Entity
  169 + expose :name
  170 + end
  171 +
  172 + class Tag < Entity
  173 + root 'tags', 'tag'
  174 + expose :name
  175 + end
  176 +
  177 +
119 end 178 end
120 end 179 end
121 end 180 end
lib/noosfero/api/helpers.rb
1 -module Noosfero  
2 - module API  
3 - module APIHelpers 1 +require 'grape'
  2 +require_relative '../../find_by_contents'
  3 +
  4 + module Noosfero;
  5 + module API
  6 + module APIHelpers
4 PRIVATE_TOKEN_PARAM = :private_token 7 PRIVATE_TOKEN_PARAM = :private_token
5 - DEFAULT_ALLOWED_PARAMETERS = [:parent_id, :from, :until, :content_type] 8 + DEFAULT_ALLOWED_PARAMETERS = [:parent_id, :from, :until, :content_type, :author_id]
  9 +
  10 + include SanitizeParams
  11 + include Noosfero::Plugin::HotSpot
  12 + include ForgotPasswordHelper
  13 + include SearchTermHelper
  14 +
  15 + def set_locale
  16 + I18n.locale = (params[:lang] || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en')
  17 + end
  18 +
  19 + def init_noosfero_plugins
  20 + plugins
  21 + end
6 22
7 def current_user 23 def current_user
8 private_token = (params[PRIVATE_TOKEN_PARAM] || headers['Private-Token']).to_s 24 private_token = (params[PRIVATE_TOKEN_PARAM] || headers['Private-Token']).to_s
@@ -23,6 +39,22 @@ module Noosfero @@ -23,6 +39,22 @@ module Noosfero
23 @environment 39 @environment
24 end 40 end
25 41
  42 + include FindByContents
  43 +
  44 + ####################################################################
  45 + #### SEARCH
  46 + ####################################################################
  47 + def multiple_search?(searches=nil)
  48 + ['index', 'category_index'].include?(params[:action]) || (searches && searches.size > 1)
  49 + end
  50 + ####################################################################
  51 +
  52 + def logger
  53 + logger = Logger.new(File.join(Rails.root, 'log', "#{ENV['RAILS_ENV'] || 'production'}_api.log"))
  54 + logger.formatter = GrapeLogging::Formatters::Default.new
  55 + logger
  56 + end
  57 +
26 def limit 58 def limit
27 limit = params[:limit].to_i 59 limit = params[:limit].to_i
28 limit = default_limit if limit <= 0 60 limit = default_limit if limit <= 0
@@ -50,7 +82,7 @@ module Noosfero @@ -50,7 +82,7 @@ module Noosfero
50 82
51 def find_article(articles, id) 83 def find_article(articles, id)
52 article = articles.find(id) 84 article = articles.find(id)
53 - article.display_to?(current_user.person) ? article : forbidden! 85 + article.display_to?(current_person) ? article : forbidden!
54 end 86 end
55 87
56 def post_article(asset, params) 88 def post_article(asset, params)
@@ -75,12 +107,25 @@ module Noosfero @@ -75,12 +107,25 @@ module Noosfero
75 present article, :with => Entities::Article, :fields => params[:fields] 107 present article, :with => Entities::Article, :fields => params[:fields]
76 end 108 end
77 109
78 - def present_articles(asset)  
79 - articles = select_filtered_collection_of(asset, 'articles', params)  
80 - articles = articles.display_filter(current_person, nil) 110 + def present_articles_for_asset(asset, method = 'articles')
  111 + articles = find_articles(asset, method)
  112 + present_articles(articles)
  113 + end
  114 +
  115 + def present_articles(articles)
81 present articles, :with => Entities::Article, :fields => params[:fields] 116 present articles, :with => Entities::Article, :fields => params[:fields]
82 end 117 end
83 118
  119 + def find_articles(asset, method = 'articles')
  120 + articles = select_filtered_collection_of(asset, method, params)
  121 + if current_person.present?
  122 + articles = articles.display_filter(current_person, nil)
  123 + else
  124 + articles = articles.published
  125 + end
  126 + articles
  127 + end
  128 +
84 def find_task(asset, id) 129 def find_task(asset, id)
85 task = asset.tasks.find(id) 130 task = asset.tasks.find(id)
86 current_person.has_permission?(task.permission, asset) ? task : forbidden! 131 current_person.has_permission?(task.permission, asset) ? task : forbidden!
@@ -127,8 +172,27 @@ module Noosfero @@ -127,8 +172,27 @@ module Noosfero
127 conditions 172 conditions
128 end 173 end
129 174
130 - def make_order_with_parameters(params)  
131 - params[:order] || "created_at DESC" 175 + # changing make_order_with_parameters to avoid sql injection
  176 + def make_order_with_parameters(object, method, params)
  177 + order = "created_at DESC"
  178 + unless params[:order].blank?
  179 + if params[:order].include? '\'' or params[:order].include? '"'
  180 + order = "created_at DESC"
  181 + elsif ['RANDOM()', 'RANDOM'].include? params[:order].upcase
  182 + order = 'RANDOM()'
  183 + else
  184 + field_name, direction = params[:order].split(' ')
  185 + assoc = object.class.reflect_on_association(method.to_sym)
  186 + if !field_name.blank? and assoc
  187 + if assoc.klass.attribute_names.include? field_name
  188 + if direction.present? and ['ASC','DESC'].include? direction.upcase
  189 + order = "#{field_name} #{direction.upcase}"
  190 + end
  191 + end
  192 + end
  193 + end
  194 + end
  195 + return order
132 end 196 end
133 197
134 def make_page_number_with_parameters(params) 198 def make_page_number_with_parameters(params)
@@ -152,25 +216,36 @@ module Noosfero @@ -152,25 +216,36 @@ module Noosfero
152 end 216 end
153 217
154 def by_reference(scope, params) 218 def by_reference(scope, params)
155 - if params[:reference_id]  
156 - created_at = scope.find(params[:reference_id]).created_at  
157 - scope.send("#{params.key?(:oldest) ? 'older_than' : 'younger_than'}", created_at) 219 + reference_id = params[:reference_id].to_i == 0 ? nil : params[:reference_id].to_i
  220 + if reference_id.nil?
  221 + scope
158 else 222 else
  223 + created_at = scope.find(reference_id).created_at
  224 + scope.send("#{params.key?(:oldest) ? 'older_than' : 'younger_than'}", created_at)
  225 + end
  226 + end
  227 +
  228 + def by_categories(scope, params)
  229 + category_ids = params[:category_ids]
  230 + if category_ids.nil?
159 scope 231 scope
  232 + else
  233 + scope.joins(:categories).where(:categories => {:id => category_ids})
160 end 234 end
161 end 235 end
162 236
163 def select_filtered_collection_of(object, method, params) 237 def select_filtered_collection_of(object, method, params)
164 conditions = make_conditions_with_parameter(params) 238 conditions = make_conditions_with_parameter(params)
165 - order = make_order_with_parameters(params) 239 + order = make_order_with_parameters(object,method,params)
166 page_number = make_page_number_with_parameters(params) 240 page_number = make_page_number_with_parameters(params)
167 per_page = make_per_page_with_parameters(params) 241 per_page = make_per_page_with_parameters(params)
168 timestamp = make_timestamp_with_parameters_and_method(params, method) 242 timestamp = make_timestamp_with_parameters_and_method(params, method)
169 243
170 objects = object.send(method) 244 objects = object.send(method)
171 objects = by_reference(objects, params) 245 objects = by_reference(objects, params)
  246 + objects = by_categories(objects, params)
172 247
173 - objects = objects.where(conditions).where(timestamp).page(page_number).per_page(per_page).order(order) 248 + objects = objects.where(conditions).where(timestamp).page(page_number).per_page(per_page).reorder(order)
174 249
175 objects 250 objects
176 end 251 end
@@ -179,6 +254,7 @@ module Noosfero @@ -179,6 +254,7 @@ module Noosfero
179 unauthorized! unless current_user 254 unauthorized! unless current_user
180 end 255 end
181 256
  257 +
182 # Checks the occurrences of uniqueness of attributes, each attribute must be present in the params hash 258 # Checks the occurrences of uniqueness of attributes, each attribute must be present in the params hash
183 # or a Bad Request error is invoked. 259 # or a Bad Request error is invoked.
184 # 260 #
@@ -198,20 +274,6 @@ module Noosfero @@ -198,20 +274,6 @@ module Noosfero
198 attrs 274 attrs
199 end 275 end
200 276
201 - def verify_recaptcha_v2(remote_ip, g_recaptcha_response, private_key, api_recaptcha_verify_uri)  
202 - verify_hash = {  
203 - "secret" => private_key,  
204 - "remoteip" => remote_ip,  
205 - "response" => g_recaptcha_response  
206 - }  
207 - uri = URI(api_recaptcha_verify_uri)  
208 - https = Net::HTTP.new(uri.host, uri.port)  
209 - https.use_ssl = true  
210 - request = Net::HTTP::Post.new(uri.path)  
211 - request.set_form_data(verify_hash)  
212 - JSON.parse(https.request(request).body)  
213 - end  
214 -  
215 ########################################## 277 ##########################################
216 # error helpers # 278 # error helpers #
217 ########################################## 279 ##########################################
@@ -247,13 +309,22 @@ module Noosfero @@ -247,13 +309,22 @@ module Noosfero
247 render_api_error!(_('Method Not Allowed'), 405) 309 render_api_error!(_('Method Not Allowed'), 405)
248 end 310 end
249 311
250 - def render_api_error!(message, status)  
251 - error!({'message' => message, :code => status}, status) 312 + # javascript_console_message is supposed to be executed as console.log()
  313 + def render_api_error!(user_message, status, log_message = nil, javascript_console_message = nil)
  314 + message_hash = {'message' => user_message, :code => status}
  315 + message_hash[:javascript_console_message] = javascript_console_message if javascript_console_message.present?
  316 + log_msg = "#{status}, User message: #{user_message}"
  317 + log_msg = "#{log_message}, #{log_msg}" if log_message.present?
  318 + log_msg = "#{log_msg}, Javascript Console Message: #{javascript_console_message}" if javascript_console_message.present?
  319 + logger.error log_msg unless Rails.env.test?
  320 + error!(message_hash, status)
252 end 321 end
253 322
254 def render_api_errors!(messages) 323 def render_api_errors!(messages)
  324 + messages = messages.to_a if messages.class == ActiveModel::Errors
255 render_api_error!(messages.join(','), 400) 325 render_api_error!(messages.join(','), 400)
256 end 326 end
  327 +
257 protected 328 protected
258 329
259 def set_session_cookie 330 def set_session_cookie
@@ -278,7 +349,7 @@ module Noosfero @@ -278,7 +349,7 @@ module Noosfero
278 end 349 end
279 350
280 def filter_disabled_plugins_endpoints 351 def filter_disabled_plugins_endpoints
281 - not_found! if Noosfero::API::API.endpoint_unavailable?(self, !@environment) 352 + not_found! if Noosfero::API::API.endpoint_unavailable?(self, @environment)
282 end 353 end
283 354
284 private 355 private
@@ -305,7 +376,6 @@ module Noosfero @@ -305,7 +376,6 @@ module Noosfero
305 def period(from_date, until_date) 376 def period(from_date, until_date)
306 begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date 377 begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date
307 end_period = until_date.nil? ? DateTime.now : until_date 378 end_period = until_date.nil? ? DateTime.now : until_date
308 -  
309 begin_period..end_period 379 begin_period..end_period
310 end 380 end
311 381
lib/noosfero/api/session.rb
@@ -2,7 +2,6 @@ require &quot;uri&quot; @@ -2,7 +2,6 @@ require &quot;uri&quot;
2 2
3 module Noosfero 3 module Noosfero
4 module API 4 module API
5 -  
6 class Session < Grape::API 5 class Session < Grape::API
7 6
8 # Login to get token 7 # Login to get token
@@ -14,11 +13,15 @@ module Noosfero @@ -14,11 +13,15 @@ module Noosfero
14 # Example Request: 13 # Example Request:
15 # POST http://localhost:3000/api/v1/login?login=adminuser&password=admin 14 # POST http://localhost:3000/api/v1/login?login=adminuser&password=admin
16 post "/login" do 15 post "/login" do
17 - user ||= User.authenticate(params[:login], params[:password], environment) 16 + begin
  17 + user ||= User.authenticate(params[:login], params[:password], environment)
  18 + rescue NoosferoExceptions::UserNotActivated => e
  19 + render_api_error!(e.message, 401)
  20 + end
18 21
19 return unauthorized! unless user 22 return unauthorized! unless user
20 @current_user = user 23 @current_user = user
21 - present user, :with => Entities::UserLogin 24 + present user, :with => Entities::UserLogin, :current_person => current_person
22 end 25 end
23 26
24 # Create user. 27 # Create user.
@@ -28,34 +31,103 @@ module Noosfero @@ -28,34 +31,103 @@ module Noosfero
28 # password (required) - Password 31 # password (required) - Password
29 # login - login 32 # login - login
30 # Example Request: 33 # Example Request:
31 - # POST /register?email=some@mail.com&password=pas&login=some 34 + # POST /register?email=some@mail.com&password=pas&password_confirmation=pas&login=some
32 params do 35 params do
33 requires :email, type: String, desc: _("Email") 36 requires :email, type: String, desc: _("Email")
34 requires :login, type: String, desc: _("Login") 37 requires :login, type: String, desc: _("Login")
35 requires :password, type: String, desc: _("Password") 38 requires :password, type: String, desc: _("Password")
36 end 39 end
  40 +
37 post "/register" do 41 post "/register" do
38 - unique_attributes! User, [:email, :login]  
39 - attrs = attributes_for_keys [:email, :login, :password]  
40 - attrs[:password_confirmation] = attrs[:password]  
41 -  
42 - #Commented for stress tests  
43 -  
44 - # remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR'])  
45 - # private_key = API.NOOSFERO_CONF['api_recaptcha_private_key']  
46 - # api_recaptcha_verify_uri = API.NOOSFERO_CONF['api_recaptcha_verify_uri']  
47 - # captcha_result = verify_recaptcha_v2(remote_ip, params['g-recaptcha-response'], private_key, api_recaptcha_verify_uri)  
48 - user = User.new(attrs)  
49 -# if captcha_result["success"] and user.save  
50 - if user.save  
51 - user.activate  
52 - user.generate_private_token!  
53 - present user, :with => Entities::UserLogin  
54 - else  
55 - message = user.errors.to_json 42 + attrs = attributes_for_keys [:email, :login, :password, :password_confirmation] + environment.signup_person_fields
  43 + name = params[:name].present? ? params[:name] : attrs[:email]
  44 + attrs[:password_confirmation] = attrs[:password] if !attrs.has_key?(:password_confirmation)
  45 + user = User.new(attrs.merge(:name => name))
  46 +
  47 + begin
  48 + user.signup!
  49 + user.generate_private_token! if user.activated?
  50 + present user, :with => Entities::UserLogin, :current_person => current_person
  51 + rescue ActiveRecord::RecordInvalid
  52 + message = user.errors.as_json.merge((user.person.present? ? user.person.errors : {}).as_json).to_json
56 render_api_error!(message, 400) 53 render_api_error!(message, 400)
57 end 54 end
58 end 55 end
  56 +
  57 + params do
  58 + requires :activation_code, type: String, desc: _("Activation token")
  59 + end
  60 +
  61 + # Activate a user.
  62 + #
  63 + # Parameter:
  64 + # activation_code (required) - Activation token
  65 + # Example Request:
  66 + # PATCH /activate?activation_code=28259abd12cc6a64ef9399cf3286cb998b96aeaf
  67 + patch "/activate" do
  68 + user = User.find_by_activation_code(params[:activation_code])
  69 + if user
  70 + unless user.environment.enabled?('admin_must_approve_new_users')
  71 + if user.activate
  72 + user.generate_private_token!
  73 + present user, :with => Entities::UserLogin, :current_person => current_person
  74 + end
  75 + else
  76 + if user.create_moderate_task
  77 + user.activation_code = nil
  78 + user.save!
  79 +
  80 + # Waiting for admin moderate user registration
  81 + status 202
  82 + body({
  83 + :message => 'Waiting for admin moderate user registration'
  84 + })
  85 + end
  86 + end
  87 + else
  88 + # Token not found in database
  89 + render_api_error!(_('Token is invalid'), 412)
  90 + end
  91 + end
  92 +
  93 + # Request a new password.
  94 + #
  95 + # Parameters:
  96 + # value (required) - Email or login
  97 + # Example Request:
  98 + # POST /forgot_password?value=some@mail.com
  99 + post "/forgot_password" do
  100 + requestors = fetch_requestors(params[:value])
  101 + not_found! if requestors.blank?
  102 + remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR'])
  103 + requestors.each do |requestor|
  104 + ChangePassword.create!(:requestor => requestor)
  105 + end
  106 + end
  107 +
  108 + params do
  109 + requires :code, type: String, desc: _("Forgot password code")
  110 + end
  111 + # Change password
  112 + #
  113 + # Parameters:
  114 + # code (required) - Change password code
  115 + # password (required)
  116 + # password_confirmation (required)
  117 + # Example Request:
  118 + # PATCH /new_password?code=xxxx&password=secret&password_confirmation=secret
  119 + patch "/new_password" do
  120 + change_password = ChangePassword.find_by_code(params[:code])
  121 + not_found! if change_password.nil?
  122 +
  123 + if change_password.update_attributes(:password => params[:password], :password_confirmation => params[:password_confirmation])
  124 + change_password.finish
  125 + present change_password.requestor.user, :with => Entities::UserLogin, :current_person => current_person
  126 + else
  127 + something_wrong!
  128 + end
  129 + end
  130 +
59 end 131 end
60 end 132 end
61 end 133 end
lib/noosfero/api/v1/articles.rb
@@ -2,7 +2,6 @@ module Noosfero @@ -2,7 +2,6 @@ module Noosfero
2 module API 2 module API
3 module V1 3 module V1
4 class Articles < Grape::API 4 class Articles < Grape::API
5 - before { authenticate! }  
6 5
7 ARTICLE_TYPES = Article.descendants.map{|a| a.to_s} 6 ARTICLE_TYPES = Article.descendants.map{|a| a.to_s}
8 7
@@ -17,39 +16,148 @@ module Noosfero @@ -17,39 +16,148 @@ module Noosfero
17 # 16 #
18 # Example Request: 17 # Example Request:
19 # GET host/api/v1/articles?from=2013-04-04-14:41:43&until=2015-04-04-14:41:43&limit=10&private_token=e96fff37c2238fdab074d1dcea8e6317 18 # GET host/api/v1/articles?from=2013-04-04-14:41:43&until=2015-04-04-14:41:43&limit=10&private_token=e96fff37c2238fdab074d1dcea8e6317
  19 +
  20 + desc 'Return all articles of all kinds' do
  21 + detail 'Get all articles filtered by fields in query params'
  22 + params Noosfero::API::Entities::Article.documentation
  23 + success Noosfero::API::Entities::Article
  24 + failure [[403, 'Forbidden']]
  25 + named 'ArticlesList'
  26 + headers [
  27 + 'Per-Page' => {
  28 + description: 'Total number of records',
  29 + required: false
  30 + }
  31 + ]
  32 + end
20 get do 33 get do
21 - present_articles(environment) 34 + present_articles_for_asset(environment)
22 end 35 end
23 36
24 - desc "Return the article id"  
25 - get ':id' do 37 + desc "Return one article by id" do
  38 + detail 'Get only one article by id. If not found the "forbidden" http error is showed'
  39 + params Noosfero::API::Entities::Article.documentation
  40 + success Noosfero::API::Entities::Article
  41 + failure [[403, 'Forbidden']]
  42 + named 'ArticleById'
  43 + end
  44 + get ':id', requirements: {id: /[0-9]+/} do
26 present_article(environment) 45 present_article(environment)
27 end 46 end
28 47
  48 + post ':id' do
  49 + article = environment.articles.find(params[:id])
  50 + return forbidden! unless article.allow_edit?(current_person)
  51 + article.update_attributes!(params[:article])
  52 + present article, :with => Entities::Article, :fields => params[:fields]
  53 + end
  54 +
  55 + desc 'Report a abuse and/or violent content in a article by id' do
  56 + detail 'Submit a abuse (in general, a content violation) report about a specific article'
  57 + params Noosfero::API::Entities::Article.documentation
  58 + failure [[400, 'Bad Request']]
  59 + named 'ArticleReportAbuse'
  60 + end
  61 + post ':id/report_abuse' do
  62 + article = find_article(environment.articles, params[:id])
  63 + profile = article.profile
  64 + begin
  65 + abuse_report = AbuseReport.new(:reason => params[:report_abuse])
  66 + if !params[:content_type].blank?
  67 + article = params[:content_type].constantize.find(params[:content_id])
  68 + abuse_report.content = article_reported_version(article)
  69 + end
  70 +
  71 + current_person.register_report(abuse_report, profile)
  72 +
  73 + if !params[:content_type].blank?
  74 + abuse_report = AbuseReport.find_by_reporter_id_and_abuse_complaint_id(current_person.id, profile.opened_abuse_complaint.id)
  75 + Delayed::Job.enqueue DownloadReportedImagesJob.new(abuse_report, article)
  76 + end
  77 +
  78 + {
  79 + :success => true,
  80 + :message => _('Your abuse report was registered. The administrators are reviewing your report.'),
  81 + }
  82 + rescue Exception => exception
  83 + #logger.error(exception.to_s)
  84 + render_api_error!(_('Your report couldn\'t be saved due to some problem. Please contact the administrator.'), 400)
  85 + end
  86 +
  87 + end
  88 +
  89 + desc "Returns the articles I voted" do
  90 + detail 'Get the Articles I make a vote'
  91 + failure [[403, 'Forbidden']]
  92 + named 'ArticleFollowers'
  93 + end
  94 + #FIXME refactor this method
  95 + get 'voted_by_me' do
  96 + present_articles(current_person.votes.collect(&:voteable))
  97 + end
  98 +
  99 + desc 'Perform a vote on a article by id' do
  100 + detail 'Vote on a specific article with values: 1 (if you like) or -1 (if not)'
  101 + params Noosfero::API::Entities::UserLogin.documentation
  102 + failure [[401,'Unauthorized']]
  103 + named 'ArticleVote'
  104 + end
  105 + post ':id/vote' do
  106 + authenticate!
  107 + value = (params[:value] || 1).to_i
  108 + # FIXME verify allowed values
  109 + render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value)
  110 + article = find_article(environment.articles, params[:id])
  111 + vote = Vote.new(:voteable => article, :voter => current_person, :vote => value)
  112 + {:vote => vote.save}
  113 + end
  114 +
  115 + desc 'Return the children of a article identified by id' do
  116 + detail 'Get all children articles of a specific article'
  117 + params Noosfero::API::Entities::Article.documentation
  118 + failure [[403, 'Forbidden']]
  119 + named 'ArticleChildren'
  120 + end
  121 +
29 get ':id/children' do 122 get ':id/children' do
30 article = find_article(environment.articles, params[:id]) 123 article = find_article(environment.articles, params[:id])
31 124
32 #TODO make tests for this situation 125 #TODO make tests for this situation
33 votes_order = params.delete(:order) if params[:order]=='votes_score' 126 votes_order = params.delete(:order) if params[:order]=='votes_score'
34 articles = select_filtered_collection_of(article, 'children', params) 127 articles = select_filtered_collection_of(article, 'children', params)
35 - articles = articles.display_filter(current_person, nil)  
36 - 128 + articles = articles.display_filter(current_person, article.profile)
37 129
38 #TODO make tests for this situation 130 #TODO make tests for this situation
39 if votes_order 131 if votes_order
40 articles = articles.joins('left join votes on articles.id=votes.voteable_id').group('articles.id').reorder('sum(coalesce(votes.vote, 0)) DESC') 132 articles = articles.joins('left join votes on articles.id=votes.voteable_id').group('articles.id').reorder('sum(coalesce(votes.vote, 0)) DESC')
41 end 133 end
42 -  
43 Article.hit(articles) 134 Article.hit(articles)
44 - present articles, :with => Entities::Article, :fields => params[:fields] 135 + present_articles(articles)
45 end 136 end
46 137
  138 + desc 'Return one child of a article identified by id' do
  139 + detail 'Get a child of a specific article'
  140 + params Noosfero::API::Entities::Article.documentation
  141 + success Noosfero::API::Entities::Article
  142 + failure [[403, 'Forbidden']]
  143 + named 'ArticleChild'
  144 + end
47 get ':id/children/:child_id' do 145 get ':id/children/:child_id' do
48 article = find_article(environment.articles, params[:id]) 146 article = find_article(environment.articles, params[:id])
49 - present find_article(article.children, params[:child_id]), :with => Entities::Article, :fields => params[:fields] 147 + child = find_article(article.children, params[:child_id])
  148 + child.hit
  149 + present child, :with => Entities::Article, :fields => params[:fields]
50 end 150 end
51 151
  152 + desc 'Suggest a article to another profile' do
  153 + detail 'Suggest a article to another profile (person, community...)'
  154 + params Noosfero::API::Entities::Article.documentation
  155 + success Noosfero::API::Entities::Task
  156 + failure [[401,'Unauthorized']]
  157 + named 'ArticleSuggest'
  158 + end
52 post ':id/children/suggest' do 159 post ':id/children/suggest' do
  160 + authenticate!
53 parent_article = environment.articles.find(params[:id]) 161 parent_article = environment.articles.find(params[:id])
54 162
55 suggest_article = SuggestArticle.new 163 suggest_article = SuggestArticle.new
@@ -66,8 +174,15 @@ module Noosfero @@ -66,8 +174,15 @@ module Noosfero
66 174
67 # Example Request: 175 # Example Request:
68 # POST api/v1/articles/:id/children?private_token=234298743290432&article[name]=title&article[body]=body 176 # POST api/v1/articles/:id/children?private_token=234298743290432&article[name]=title&article[body]=body
  177 + desc 'Add a child article to a parent identified by id' do
  178 + detail 'Create a new article and associate to a parent'
  179 + params Noosfero::API::Entities::Article.documentation
  180 + success Noosfero::API::Entities::Article
  181 + failure [[401,'Unauthorized']]
  182 + named 'ArticleAddChild'
  183 + end
69 post ':id/children' do 184 post ':id/children' do
70 - 185 + authenticate!
71 parent_article = environment.articles.find(params[:id]) 186 parent_article = environment.articles.find(params[:id])
72 return forbidden! unless parent_article.allow_create?(current_person) 187 return forbidden! unless parent_article.allow_create?(current_person)
73 188
@@ -95,11 +210,37 @@ module Noosfero @@ -95,11 +210,37 @@ module Noosfero
95 resource kind.pluralize.to_sym do 210 resource kind.pluralize.to_sym do
96 segment "/:#{kind}_id" do 211 segment "/:#{kind}_id" do
97 resource :articles do 212 resource :articles do
  213 +
  214 + desc "Return all articles associate with a profile of type #{kind}" do
  215 + detail 'Get a list of articles of a profile'
  216 + params Noosfero::API::Entities::Article.documentation
  217 + success Noosfero::API::Entities::Article
  218 + failure [[403, 'Forbidden']]
  219 + named 'ArticlesOfProfile'
  220 + end
98 get do 221 get do
99 profile = environment.send(kind.pluralize).find(params["#{kind}_id"]) 222 profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
100 - present_articles(profile) 223 +
  224 + if params[:path].present?
  225 + article = profile.articles.find_by_path(params[:path])
  226 + if !article || !article.display_to?(current_person)
  227 + article = forbidden!
  228 + end
  229 +
  230 + present article, :with => Entities::Article, :fields => params[:fields]
  231 + else
  232 +
  233 + present_articles_for_asset(profile)
  234 + end
101 end 235 end
102 236
  237 + desc "Return a article associate with a profile of type #{kind}" do
  238 + detail 'Get only one article of a profile'
  239 + params Noosfero::API::Entities::Article.documentation
  240 + success Noosfero::API::Entities::Article
  241 + failure [[403, 'Forbidden']]
  242 + named 'ArticleOfProfile'
  243 + end
103 get ':id' do 244 get ':id' do
104 profile = environment.send(kind.pluralize).find(params["#{kind}_id"]) 245 profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
105 present_article(profile) 246 present_article(profile)
@@ -107,6 +248,13 @@ module Noosfero @@ -107,6 +248,13 @@ module Noosfero
107 248
108 # Example Request: 249 # Example Request:
109 # POST api/v1/{people,communities,enterprises}/:asset_id/articles?private_token=234298743290432&article[name]=title&article[body]=body 250 # POST api/v1/{people,communities,enterprises}/:asset_id/articles?private_token=234298743290432&article[name]=title&article[body]=body
  251 + desc "Add a new article associated with a profile of type #{kind}" do
  252 + detail 'Create a new article and associate with a profile'
  253 + params Noosfero::API::Entities::Article.documentation
  254 + success Noosfero::API::Entities::Article
  255 + failure [[403, 'Forbidden']]
  256 + named 'ArticleCreateToProfile'
  257 + end
110 post do 258 post do
111 profile = environment.send(kind.pluralize).find(params["#{kind}_id"]) 259 profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
112 post_article(profile, params) 260 post_article(profile, params)
lib/noosfero/api/v1/comments.rb
@@ -30,8 +30,8 @@ module Noosfero @@ -30,8 +30,8 @@ module Noosfero
30 # POST api/v1/articles/12/comments?private_token=2298743290432&body=new comment&title=New 30 # POST api/v1/articles/12/comments?private_token=2298743290432&body=new comment&title=New
31 post ":id/comments" do 31 post ":id/comments" do
32 article = find_article(environment.articles, params[:id]) 32 article = find_article(environment.articles, params[:id])
33 - options = params.select { |key,v| !['id','private_token'].include?(key) }.merge(:author => current_person)  
34 - present article.comments.create(options), :with => Entities::Comment 33 + options = params.select { |key,v| !['id','private_token'].include?(key) }.merge(:author => current_person, :source => article)
  34 + present Comment.create(options), :with => Entities::Comment
35 end 35 end
36 end 36 end
37 37
lib/noosfero/api/v1/contacts.rb 0 → 100644
@@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
  1 +module Noosfero
  2 + module API
  3 + module V1
  4 + class Contacts < Grape::API
  5 +
  6 + resource :communities do
  7 +
  8 + resource ':id/contact' do
  9 + #contact => {:name => 'some name', :email => 'test@mail.com', :subject => 'some title', :message => 'some message'}
  10 + desc "Send a contact message"
  11 + post do
  12 + profile = environment.communities.find(params[:id])
  13 + forbidden! unless profile.present?
  14 + contact = Contact.new params[:contact].merge(dest: profile)
  15 + if contact.deliver
  16 + {:success => true}
  17 + else
  18 + {:success => false}
  19 + end
  20 + end
  21 +
  22 + end
  23 + end
  24 +
  25 + end
  26 + end
  27 + end
  28 +end
lib/noosfero/api/v1/environments.rb 0 → 100644
@@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
  1 +module Noosfero
  2 + module API
  3 + module V1
  4 + class Environments < Grape::API
  5 +
  6 + resource :environment do
  7 +
  8 + desc "Return the person information"
  9 + get '/signup_person_fields' do
  10 + present environment.signup_person_fields
  11 + end
  12 +
  13 + end
  14 +
  15 + end
  16 + end
  17 + end
  18 +end
lib/noosfero/api/v1/people.rb
@@ -48,6 +48,13 @@ module Noosfero @@ -48,6 +48,13 @@ module Noosfero
48 present person, :with => Entities::Person 48 present person, :with => Entities::Person
49 end 49 end
50 50
  51 + desc "Update person information"
  52 + post ':id' do
  53 + return forbidden! if current_person.id.to_s != params[:id]
  54 + current_person.update_attributes!(params[:person])
  55 + present current_person, :with => Entities::Person
  56 + end
  57 +
51 # Example Request: 58 # Example Request:
52 # POST api/v1/people?person[login]=some_login&person[password]=some_password&person[name]=Jack 59 # POST api/v1/people?person[login]=some_login&person[password]=some_password&person[name]=Jack
53 desc "Create person" 60 desc "Create person"
lib/noosfero/api/v1/search.rb 0 → 100644
@@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
  1 +module Noosfero
  2 + module API
  3 + module V1
  4 + class Search < Grape::API
  5 +
  6 + resource :search do
  7 + resource :article do
  8 + get do
  9 + # Security checks
  10 + sanitize_params_hash(params)
  11 + # APIHelpers
  12 + asset = :articles
  13 + context = environment
  14 +
  15 + profile = environment.profiles.find(params[:profile_id]) if params[:profile_id]
  16 + scope = profile.nil? ? environment.articles.public : profile.articles.public
  17 + scope = scope.where(:type => params[:type]) if params[:type] && !(params[:type] == 'Article')
  18 + scope = scope.where(:parent_id => params[:parent_id]) if params[:parent_id].present?
  19 + scope = scope.joins(:categories).where(:categories => {:id => params[:category_ids]}) if params[:category_ids].present?
  20 + query = params[:query] || ""
  21 + order = "more_recent"
  22 +
  23 + options = {:filter => order, :template_id => params[:template_id]}
  24 +
  25 + paginate_options = params.select{|k,v| [:page, :per_page].include?(k.to_sym)}
  26 + paginate_options.each_pair{|k,v| v=v.to_i}
  27 + paginate_options[:page]=1 if !paginate_options.keys.include?(:page)
  28 +
  29 + search_result = find_by_contents(asset, context, scope, query, paginate_options.symbolize_keys, options)
  30 +
  31 + articles = search_result[:results]
  32 +
  33 + result = present_articles(articles)
  34 +
  35 + result
  36 + end
  37 + end
  38 + end
  39 +
  40 + end
  41 + end
  42 + end
  43 +end
lib/noosfero/api/v1/tags.rb 0 → 100644
@@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
  1 +module Noosfero
  2 + module API
  3 + module V1
  4 + class Tags < Grape::API
  5 + before { authenticate! }
  6 +
  7 + resource :articles do
  8 +
  9 + resource ':id/tags' do
  10 +
  11 + get do
  12 + article = find_article(environment.articles, params[:id])
  13 + present article.tag_list
  14 + end
  15 +
  16 + desc "Add a tag to an article"
  17 + post do
  18 + article = find_article(environment.articles, params[:id])
  19 + article.tag_list=params[:tags]
  20 + article.save
  21 + present article.tag_list
  22 + end
  23 +
  24 + end
  25 + end
  26 +
  27 + end
  28 + end
  29 + end
  30 +end
lib/noosfero/api/v1/users.rb 0 → 100644
@@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
  1 +module Noosfero
  2 + module API
  3 + module V1
  4 + class Users < Grape::API
  5 + before { authenticate! }
  6 +
  7 + resource :users do
  8 +
  9 + get do
  10 + users = select_filtered_collection_of(environment, 'users', params)
  11 + users = users.select{|u| u.person.display_info_to? current_person}
  12 + present users, :with => Entities::User, :current_person => current_person
  13 + end
  14 +
  15 + # Example Request:
  16 + # POST api/v1/users?user[login]=some_login&user[password]=some
  17 + post do
  18 + user = User.new(params[:user])
  19 + user.terms_of_use = environment.terms_of_use
  20 + user.environment = environment
  21 + if !user.save
  22 + render_api_errors!(user.errors.full_messages)
  23 + end
  24 +
  25 + present user, :with => Entities::User, :current_person => current_person
  26 + end
  27 +
  28 + get "/me" do
  29 + present current_user, :with => Entities::User, :current_person => current_person
  30 + end
  31 +
  32 + get ":id" do
  33 + user = environment.users.find_by_id(params[:id])
  34 + unless user.person.display_info_to? current_person
  35 + unauthorized!
  36 + end
  37 + present user, :with => Entities::User, :current_person => current_person
  38 + end
  39 +
  40 + get ":id/permissions" do
  41 + user = environment.users.find(params[:id])
  42 + output = {}
  43 + user.person.role_assignments.map do |role_assigment|
  44 + if role_assigment.resource.respond_to?(:identifier) && role_assigment.resource.identifier == params[:profile]
  45 + output[:permissions] = role_assigment.role.permissions
  46 + end
  47 + end
  48 + present output
  49 + end
  50 +
  51 + end
  52 +
  53 + end
  54 + end
  55 + end
  56 +end
lib/sanitize_params.rb 0 → 100644
@@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
  1 +module SanitizeParams
  2 +
  3 + protected
  4 +
  5 + # Check each request parameter for
  6 + # improper HTML or Script tags
  7 + def sanitize_params
  8 + sanitize_params_hash(request.params)
  9 + end
  10 +
  11 + # Given a params list sanitize all
  12 + def sanitize_params_hash(params)
  13 + params.each { |k, v|
  14 + if v.is_a?(String)
  15 + params[k] = sanitize_param v
  16 + elsif v.is_a?(Array)
  17 + params[k] = sanitize_array v
  18 + elsif v.kind_of?(Hash)
  19 + params[k] = sanitize_params_hash(v)
  20 + end
  21 + }
  22 + end
  23 +
  24 + # If the parameter was an array,
  25 + # try to sanitize each element in the array
  26 + def sanitize_array(array)
  27 + array.map! { |e|
  28 + if e.is_a?(String)
  29 + sanitize_param e
  30 + end
  31 + }
  32 + return array
  33 + end
  34 +
  35 + # Santitize a single value
  36 + def sanitize_param(value)
  37 + allowed_tags = %w(a acronym b strong i em li ul ol h1 h2 h3 h4 h5 h6 blockquote br cite sub sup ins p)
  38 + ActionController::Base.helpers.sanitize(value, tags: allowed_tags, attributes: %w(href title))
  39 + end
  40 +
  41 +end
test/unit/api/articles_test.rb
@@ -31,7 +31,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -31,7 +31,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase
31 end 31 end
32 32
33 should 'not return article if user has no permission to view it' do 33 should 'not return article if user has no permission to view it' do
34 - person = fast_create(Person) 34 + person = fast_create(Person, :environment_id => environment.id)
35 article = fast_create(Article, :profile_id => person.id, :name => "Some thing", :published => false) 35 article = fast_create(Article, :profile_id => person.id, :name => "Some thing", :published => false)
36 assert !article.published? 36 assert !article.published?
37 37
@@ -48,8 +48,17 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -48,8 +48,17 @@ class ArticlesTest &lt; ActiveSupport::TestCase
48 assert_equivalent [child1.id, child2.id], json["articles"].map { |a| a["id"] } 48 assert_equivalent [child1.id, child2.id], json["articles"].map { |a| a["id"] }
49 end 49 end
50 50
  51 + should 'list public article children for not logged in access' do
  52 + article = fast_create(Article, :profile_id => user.person.id, :name => "Some thing")
  53 + child1 = fast_create(Article, :parent_id => article.id, :profile_id => user.person.id, :name => "Some thing")
  54 + child2 = fast_create(Article, :parent_id => article.id, :profile_id => user.person.id, :name => "Some thing")
  55 + get "/api/v1/articles/#{article.id}/children"
  56 + json = JSON.parse(last_response.body)
  57 + assert_equivalent [child1.id, child2.id], json["articles"].map { |a| a["id"] }
  58 + end
  59 +
51 should 'not list children of forbidden article' do 60 should 'not list children of forbidden article' do
52 - person = fast_create(Person) 61 + person = fast_create(Person, :environment_id => environment.id)
53 article = fast_create(Article, :profile_id => person.id, :name => "Some thing", :published => false) 62 article = fast_create(Article, :profile_id => person.id, :name => "Some thing", :published => false)
54 child1 = fast_create(Article, :parent_id => article.id, :profile_id => person.id, :name => "Some thing") 63 child1 = fast_create(Article, :parent_id => article.id, :profile_id => person.id, :name => "Some thing")
55 child2 = fast_create(Article, :parent_id => article.id, :profile_id => person.id, :name => "Some thing") 64 child2 = fast_create(Article, :parent_id => article.id, :profile_id => person.id, :name => "Some thing")
@@ -58,7 +67,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -58,7 +67,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase
58 end 67 end
59 68
60 should 'not return child of forbidden article' do 69 should 'not return child of forbidden article' do
61 - person = fast_create(Person) 70 + person = fast_create(Person, :environment_id => environment.id)
62 article = fast_create(Article, :profile_id => person.id, :name => "Some thing", :published => false) 71 article = fast_create(Article, :profile_id => person.id, :name => "Some thing", :published => false)
63 child = fast_create(Article, :parent_id => article.id, :profile_id => person.id, :name => "Some thing") 72 child = fast_create(Article, :parent_id => article.id, :profile_id => person.id, :name => "Some thing")
64 get "/api/v1/articles/#{article.id}/children/#{child.id}?#{params.to_query}" 73 get "/api/v1/articles/#{article.id}/children/#{child.id}?#{params.to_query}"
@@ -66,7 +75,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -66,7 +75,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase
66 end 75 end
67 76
68 should 'not return private child' do 77 should 'not return private child' do
69 - person = fast_create(Person) 78 + person = fast_create(Person, :environment_id => environment.id)
70 article = fast_create(Article, :profile_id => person.id, :name => "Some thing") 79 article = fast_create(Article, :profile_id => person.id, :name => "Some thing")
71 child = fast_create(Article, :parent_id => article.id, :profile_id => person.id, :name => "Some thing", :published => false) 80 child = fast_create(Article, :parent_id => article.id, :profile_id => person.id, :name => "Some thing", :published => false)
72 get "/api/v1/articles/#{article.id}/children/#{child.id}?#{params.to_query}" 81 get "/api/v1/articles/#{article.id}/children/#{child.id}?#{params.to_query}"
@@ -74,7 +83,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -74,7 +83,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase
74 end 83 end
75 84
76 should 'not list private child' do 85 should 'not list private child' do
77 - person = fast_create(Person) 86 + person = fast_create(Person, :environment_id => environment.id)
78 article = fast_create(Article, :profile_id => person.id, :name => "Some thing") 87 article = fast_create(Article, :profile_id => person.id, :name => "Some thing")
79 child = fast_create(Article, :parent_id => article.id, :profile_id => person.id, :name => "Some thing", :published => false) 88 child = fast_create(Article, :parent_id => article.id, :profile_id => person.id, :name => "Some thing", :published => false)
80 get "/api/v1/articles/#{article.id}/children?#{params.to_query}" 89 get "/api/v1/articles/#{article.id}/children?#{params.to_query}"
@@ -82,6 +91,74 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -82,6 +91,74 @@ class ArticlesTest &lt; ActiveSupport::TestCase
82 assert_not_includes json['articles'].map {|a| a['id']}, child.id 91 assert_not_includes json['articles'].map {|a| a['id']}, child.id
83 end 92 end
84 93
  94 + should 'perform a vote in a article identified by id' do
  95 + article = fast_create(Article, :profile_id => @person.id, :name => "Some thing")
  96 + @params[:value] = 1
  97 +
  98 + post "/api/v1/articles/#{article.id}/vote?#{params.to_query}"
  99 + json = JSON.parse(last_response.body)
  100 +
  101 + assert_not_equal 401, last_response.status
  102 + assert_equal true, json['vote']
  103 + end
  104 +
  105 + expose_attributes = %w(id body abstract created_at title author profile categories image votes_for votes_against setting position hits start_date end_date tag_list parent children children_count)
  106 +
  107 + expose_attributes.each do |attr|
  108 + should "expose article #{attr} attribute by default" do
  109 + article = fast_create(Article, :profile_id => user.person.id, :name => "Some thing")
  110 + get "/api/v1/articles/?#{params.to_query}"
  111 + json = JSON.parse(last_response.body)
  112 + assert json["articles"].last.has_key?(attr)
  113 + end
  114 + end
  115 +
  116 + should "update body of article created by me" do
  117 + new_value = "Another body"
  118 + params[:article] = {:body => new_value}
  119 + article = fast_create(Article, :profile_id => person.id)
  120 + post "/api/v1/articles/#{article.id}?#{params.to_query}"
  121 + json = JSON.parse(last_response.body)
  122 + assert_equal new_value, json["article"]["body"]
  123 + end
  124 +
  125 + should "update title of article created by me" do
  126 + new_value = "Another name"
  127 + params[:article] = {:name => new_value}
  128 + article = fast_create(Article, :profile_id => person.id)
  129 + post "/api/v1/articles/#{article.id}?#{params.to_query}"
  130 + json = JSON.parse(last_response.body)
  131 + assert_equal new_value, json["article"]["title"]
  132 + end
  133 +
  134 + should 'not update article of another user' do
  135 + another_person = fast_create(Person, :environment_id => environment.id)
  136 + article = fast_create(Article, :profile_id => another_person.id)
  137 + params[:article] = {:title => 'Some title'}
  138 + post "/api/v1/articles/#{article.id}?#{params.to_query}"
  139 + assert_equal 403, last_response.status
  140 + end
  141 +
  142 + should 'not update article without permission in community' do
  143 + community = fast_create(Community, :environment_id => environment.id)
  144 + article = fast_create(Article, :profile_id => community.id)
  145 + params[:article] = {:name => 'New title'}
  146 + post "/api/v1/articles/#{article.id}?#{params.to_query}"
  147 + assert_equal 403, last_response.status
  148 + end
  149 +
  150 +
  151 + should 'update article of community if user has permission' do
  152 + community = fast_create(Community, :environment_id => environment.id)
  153 + give_permission(person, 'post_content', community)
  154 + article = fast_create(Article, :profile_id => community.id)
  155 + new_value = "Another body"
  156 + params[:article] = {:body => new_value}
  157 + post "/api/v1/articles/#{article.id}?#{params.to_query}"
  158 + json = JSON.parse(last_response.body)
  159 + assert_equal new_value, json["article"]["body"]
  160 + end
  161 +
85 should 'list articles with pagination' do 162 should 'list articles with pagination' do
86 Article.destroy_all 163 Article.destroy_all
87 article_one = fast_create(Article, :profile_id => user.person.id, :name => "Another thing", :created_at => 2.days.ago) 164 article_one = fast_create(Article, :profile_id => user.person.id, :name => "Another thing", :created_at => 2.days.ago)
@@ -126,7 +203,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -126,7 +203,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase
126 profile_kinds = %w(community person enterprise) 203 profile_kinds = %w(community person enterprise)
127 profile_kinds.each do |kind| 204 profile_kinds.each do |kind|
128 should "return article by #{kind}" do 205 should "return article by #{kind}" do
129 - profile = fast_create(kind.camelcase.constantize) 206 + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id)
130 article = fast_create(Article, :profile_id => profile.id, :name => "Some thing") 207 article = fast_create(Article, :profile_id => profile.id, :name => "Some thing")
131 get "/api/v1/#{kind.pluralize}/#{profile.id}/articles/#{article.id}?#{params.to_query}" 208 get "/api/v1/#{kind.pluralize}/#{profile.id}/articles/#{article.id}?#{params.to_query}"
132 json = JSON.parse(last_response.body) 209 json = JSON.parse(last_response.body)
@@ -134,7 +211,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -134,7 +211,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase
134 end 211 end
135 212
136 should "not return article by #{kind} if user has no permission to view it" do 213 should "not return article by #{kind} if user has no permission to view it" do
137 - profile = fast_create(kind.camelcase.constantize) 214 + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id)
138 article = fast_create(Article, :profile_id => profile.id, :name => "Some thing", :published => false) 215 article = fast_create(Article, :profile_id => profile.id, :name => "Some thing", :published => false)
139 assert !article.published? 216 assert !article.published?
140 217
@@ -143,7 +220,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -143,7 +220,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase
143 end 220 end
144 221
145 should "not list forbidden article when listing articles by #{kind}" do 222 should "not list forbidden article when listing articles by #{kind}" do
146 - profile = fast_create(kind.camelcase.constantize) 223 + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id)
147 article = fast_create(Article, :profile_id => profile.id, :name => "Some thing", :published => false) 224 article = fast_create(Article, :profile_id => profile.id, :name => "Some thing", :published => false)
148 assert !article.published? 225 assert !article.published?
149 226
@@ -151,6 +228,29 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -151,6 +228,29 @@ class ArticlesTest &lt; ActiveSupport::TestCase
151 json = JSON.parse(last_response.body) 228 json = JSON.parse(last_response.body)
152 assert_not_includes json['articles'].map {|a| a['id']}, article.id 229 assert_not_includes json['articles'].map {|a| a['id']}, article.id
153 end 230 end
  231 +
  232 + should "return article by #{kind} and path" do
  233 + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id)
  234 + parent_article = Folder.create!(:profile => profile, :name => "Parent Folder")
  235 + article = Article.create!(:profile => profile, :name => "Some thing", :parent => parent_article)
  236 +
  237 + params[:path] = parent_article.slug+'/'+article.slug
  238 + get "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}"
  239 + json = JSON.parse(last_response.body)
  240 + assert_equal article.id, json["article"]["id"]
  241 + end
  242 +
  243 + should "not return article by #{kind} and path if user has no permission to view it" do
  244 + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id)
  245 + parent_article = Folder.create!(:profile => profile, :name => "Parent Folder")
  246 + article = Article.create!(:profile => profile, :name => "Some thing", :parent => parent_article, :published => false)
  247 +
  248 + assert !article.published?
  249 +
  250 + params[:path] = parent_article.slug+'/'+article.slug
  251 + get "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}"
  252 + assert_equal 403, last_response.status
  253 + end
154 end 254 end
155 255
156 ############################# 256 #############################
@@ -160,7 +260,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -160,7 +260,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase
160 group_kinds = %w(community enterprise) 260 group_kinds = %w(community enterprise)
161 group_kinds.each do |kind| 261 group_kinds.each do |kind|
162 should "#{kind}: create article" do 262 should "#{kind}: create article" do
163 - profile = fast_create(kind.camelcase.constantize) 263 + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id)
164 give_permission(user.person, 'post_content', profile) 264 give_permission(user.person, 'post_content', profile)
165 params[:article] = {:name => "Title"} 265 params[:article] = {:name => "Title"}
166 post "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}" 266 post "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}"
@@ -169,16 +269,16 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -169,16 +269,16 @@ class ArticlesTest &lt; ActiveSupport::TestCase
169 end 269 end
170 270
171 should "#{kind}: do not create article if user has no permission to post content" do 271 should "#{kind}: do not create article if user has no permission to post content" do
172 - profile = fast_create(kind.camelcase.constantize) 272 + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id)
173 give_permission(user.person, 'invite_members', profile) 273 give_permission(user.person, 'invite_members', profile)
174 params[:article] = {:name => "Title"} 274 params[:article] = {:name => "Title"}
175 post "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}" 275 post "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}"
176 assert_equal 403, last_response.status 276 assert_equal 403, last_response.status
177 end 277 end
178 278
179 - should "#{kind}: create article with parent" do  
180 - profile = fast_create(kind.camelcase.constantize)  
181 - profile.add_member(user.person) 279 + should "#{kind} create article with parent" do
  280 + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id)
  281 + Person.any_instance.stubs(:can_post_content?).with(profile).returns(true)
182 article = fast_create(Article) 282 article = fast_create(Article)
183 283
184 params[:article] = {:name => "Title", :parent_id => article.id} 284 params[:article] = {:name => "Title", :parent_id => article.id}
@@ -187,9 +287,9 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -187,9 +287,9 @@ class ArticlesTest &lt; ActiveSupport::TestCase
187 assert_equal article.id, json["article"]["parent"]["id"] 287 assert_equal article.id, json["article"]["parent"]["id"]
188 end 288 end
189 289
190 - should "#{kind}: create article with content type passed as parameter" do  
191 - profile = fast_create(kind.camelcase.constantize)  
192 - profile.add_member(user.person) 290 + should "#{kind} create article with content type passed as parameter" do
  291 + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id)
  292 + Person.any_instance.stubs(:can_post_content?).with(profile).returns(true)
193 293
194 Article.delete_all 294 Article.delete_all
195 params[:article] = {:name => "Title"} 295 params[:article] = {:name => "Title"}
@@ -201,8 +301,8 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -201,8 +301,8 @@ class ArticlesTest &lt; ActiveSupport::TestCase
201 end 301 end
202 302
203 should "#{kind}: create article of TinyMceArticle type if no content type is passed as parameter" do 303 should "#{kind}: create article of TinyMceArticle type if no content type is passed as parameter" do
204 - profile = fast_create(kind.camelcase.constantize)  
205 - profile.add_member(user.person) 304 + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id)
  305 + Person.any_instance.stubs(:can_post_content?).with(profile).returns(true)
206 306
207 params[:article] = {:name => "Title"} 307 params[:article] = {:name => "Title"}
208 post "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}" 308 post "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}"
@@ -212,7 +312,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -212,7 +312,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase
212 end 312 end
213 313
214 should "#{kind}: not create article with invalid article content type" do 314 should "#{kind}: not create article with invalid article content type" do
215 - profile = fast_create(kind.camelcase.constantize) 315 + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id)
216 profile.add_member(user.person) 316 profile.add_member(user.person)
217 317
218 params[:article] = {:name => "Title"} 318 params[:article] = {:name => "Title"}
@@ -223,20 +323,20 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -223,20 +323,20 @@ class ArticlesTest &lt; ActiveSupport::TestCase
223 assert_equal 403, last_response.status 323 assert_equal 403, last_response.status
224 end 324 end
225 325
226 - should "#{kind}: create article defining the correct profile" do  
227 - profile = fast_create(kind.camelcase.constantize)  
228 - profile.add_member(user.person) 326 + should "#{kind} create article defining the correct profile" do
  327 + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id)
  328 + Person.any_instance.stubs(:can_post_content?).with(profile).returns(true)
229 329
230 params[:article] = {:name => "Title"} 330 params[:article] = {:name => "Title"}
231 post "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}" 331 post "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}"
232 json = JSON.parse(last_response.body) 332 json = JSON.parse(last_response.body)
233 333
234 - assert_equal profile, Article.last.profile 334 + assert_equal profile.id, json['article']['profile']['id']
235 end 335 end
236 336
237 should "#{kind}: create article defining the created_by" do 337 should "#{kind}: create article defining the created_by" do
238 - profile = fast_create(kind.camelcase.constantize)  
239 - profile.add_member(user.person) 338 + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id)
  339 + Person.any_instance.stubs(:can_post_content?).with(profile).returns(true)
240 340
241 params[:article] = {:name => "Title"} 341 params[:article] = {:name => "Title"}
242 post "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}" 342 post "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}"
@@ -246,8 +346,8 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -246,8 +346,8 @@ class ArticlesTest &lt; ActiveSupport::TestCase
246 end 346 end
247 347
248 should "#{kind}: create article defining the last_changed_by" do 348 should "#{kind}: create article defining the last_changed_by" do
249 - profile = fast_create(kind.camelcase.constantize)  
250 - profile.add_member(user.person) 349 + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id)
  350 + Person.any_instance.stubs(:can_post_content?).with(profile).returns(true)
251 351
252 params[:article] = {:name => "Title"} 352 params[:article] = {:name => "Title"}
253 post "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}" 353 post "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}"
@@ -269,7 +369,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -269,7 +369,7 @@ class ArticlesTest &lt; ActiveSupport::TestCase
269 end 369 end
270 370
271 should 'person do not create article if user has no permission to post content' do 371 should 'person do not create article if user has no permission to post content' do
272 - person = fast_create(Person) 372 + person = fast_create(Person, :environment_id => environment.id)
273 params[:article] = {:name => "Title"} 373 params[:article] = {:name => "Title"}
274 post "/api/v1/people/#{person.id}/articles?#{params.to_query}" 374 post "/api/v1/people/#{person.id}/articles?#{params.to_query}"
275 assert_equal 403, last_response.status 375 assert_equal 403, last_response.status
@@ -376,4 +476,99 @@ class ArticlesTest &lt; ActiveSupport::TestCase @@ -376,4 +476,99 @@ class ArticlesTest &lt; ActiveSupport::TestCase
376 assert_equal [0, 1, 1], [a1.reload.hits, a2.reload.hits, a3.reload.hits] 476 assert_equal [0, 1, 1], [a1.reload.hits, a2.reload.hits, a3.reload.hits]
377 end 477 end
378 478
  479 + should 'update hit attribute of article specific children' do
  480 + a1 = fast_create(Article, :profile_id => user.person.id)
  481 + a2 = fast_create(Article, :parent_id => a1.id, :profile_id => user.person.id)
  482 + get "/api/v1/articles/#{a1.id}/children/#{a2.id}?#{params.to_query}"
  483 + json = JSON.parse(last_response.body)
  484 + assert_equal 1, json['article']['hits']
  485 + end
  486 +
  487 + should 'list all events of a community in a given category' do
  488 + co = Community.create(identifier: 'my-community', name: 'name-my-community')
  489 + c1 = Category.create(environment: Environment.default, name: 'my-category')
  490 + c2 = Category.create(environment: Environment.default, name: 'dont-show-me-this-category')
  491 + e1 = fast_create(Event, :profile_id => co.id)
  492 + e2 = fast_create(Event, :profile_id => co.id)
  493 + e1.categories << c1
  494 + e2.categories << c2
  495 + e1.save!
  496 + e2.save!
  497 + params['content_type']='Event'
  498 + get "api/v1/communities/#{co.id}/articles?#{params.to_query}"
  499 + json = JSON.parse(last_response.body)
  500 + assert_equal json['articles'].count, 2
  501 + end
  502 +
  503 + should 'list a event of a community in a given category' do
  504 + co = Community.create(identifier: 'my-community', name: 'name-my-community')
  505 + c1 = Category.create(environment: Environment.default, name: 'my-category')
  506 + c2 = Category.create(environment: Environment.default, name: 'dont-show-me-this-category')
  507 + e1 = fast_create(Event, :profile_id => co.id)
  508 + e2 = fast_create(Event, :profile_id => co.id)
  509 + e1.categories << c1
  510 + e2.categories << c2
  511 + e1.save!
  512 + e2.save!
  513 + params['category_ids[]']=c1.id
  514 + params['content_type']='Event'
  515 + get "api/v1/communities/#{co.id}/articles?#{params.to_query}"
  516 + json = JSON.parse(last_response.body)
  517 + #should show only one article, since the other not in the same category
  518 + assert_equal 1, json['articles'].count
  519 + assert_equal e1.id, json['articles'][0]['id']
  520 + end
  521 +
  522 + should 'not list uncategorized event of a community if a category is given' do
  523 + co = Community.create(identifier: 'my-community', name: 'name-my-community')
  524 + c1 = Category.create(environment: Environment.default, name: 'my-category')
  525 + c2 = Category.create(environment: Environment.default, name: 'dont-show-me-this-category')
  526 + e1 = fast_create(Event, :profile_id => co.id)
  527 + e2 = fast_create(Event, :profile_id => co.id)
  528 + e3 = fast_create(Event, :profile_id => co.id)
  529 + e1.categories << c1
  530 + e2.categories << c2
  531 + params['category_ids[]']=c1.id
  532 + params['content_type']='Event'
  533 + get "api/v1/communities/#{co.id}/articles?#{params.to_query}"
  534 + json = JSON.parse(last_response.body)
  535 + assert_equal 1, json['articles'].count
  536 + assert_equal e1.id, json['articles'][0]['id']
  537 + end
  538 +
  539 + should 'list events of a community in a given 2 categories' do
  540 + co = Community.create(identifier: 'my-community', name: 'name-my-community')
  541 + c1 = Category.create(environment: Environment.default, name: 'my-category')
  542 + c2 = Category.create(environment: Environment.default, name: 'dont-show-me-this-category')
  543 + e1 = fast_create(Event, :profile_id => co.id)
  544 + e2 = fast_create(Event, :profile_id => co.id)
  545 + e1.categories << c1
  546 + e2.categories << c2
  547 + e1.save!
  548 + e2.save!
  549 + params['content_type']='Event'
  550 + params['categories_ids'] = [c1.id, c2.id]
  551 + get "api/v1/communities/#{co.id}/articles?#{params.to_query}"
  552 + json = JSON.parse(last_response.body)
  553 + assert_equal json['articles'].count, 2
  554 + end
  555 +
  556 + should 'Show 2 events since it uses an IN operator for category instead of an OR' do
  557 + co = Community.create(identifier: 'my-community', name: 'name-my-community')
  558 + c1 = Category.create(environment: Environment.default, name: 'my-category')
  559 + c2 = Category.create(environment: Environment.default, name: 'dont-show-me-this-category')
  560 + c3 = Category.create(environment: Environment.default, name: 'extra-category')
  561 + e1 = fast_create(Event, :profile_id => co.id)
  562 + e2 = fast_create(Event, :profile_id => co.id)
  563 + e1.categories << c1
  564 + e2.categories << c2
  565 + e1.save!
  566 + e2.save!
  567 + params['content_type']='Event'
  568 + params['categories_ids'] = [c1.id, c2.id, c3.id]
  569 + get "api/v1/communities/#{co.id}/articles?#{params.to_query}"
  570 + json = JSON.parse(last_response.body)
  571 + assert_equal json['articles'].count, 2
  572 + end
  573 +
379 end 574 end
test/unit/api/categories_test.rb
@@ -7,25 +7,25 @@ class CategoriesTest &lt; ActiveSupport::TestCase @@ -7,25 +7,25 @@ class CategoriesTest &lt; ActiveSupport::TestCase
7 end 7 end
8 8
9 should 'list categories' do 9 should 'list categories' do
10 - category = fast_create(Category) 10 + category = fast_create(Category, :environment_id => environment.id)
11 get "/api/v1/categories/?#{params.to_query}" 11 get "/api/v1/categories/?#{params.to_query}"
12 json = JSON.parse(last_response.body) 12 json = JSON.parse(last_response.body)
13 assert_includes json["categories"].map { |c| c["name"] }, category.name 13 assert_includes json["categories"].map { |c| c["name"] }, category.name
14 end 14 end
15 15
16 should 'get category by id' do 16 should 'get category by id' do
17 - category = fast_create(Category) 17 + category = fast_create(Category, :environment_id => environment.id)
18 get "/api/v1/categories/#{category.id}/?#{params.to_query}" 18 get "/api/v1/categories/#{category.id}/?#{params.to_query}"
19 json = JSON.parse(last_response.body) 19 json = JSON.parse(last_response.body)
20 assert_equal category.name, json["category"]["name"] 20 assert_equal category.name, json["category"]["name"]
21 end 21 end
22 22
23 should 'list parent and children when get category by id' do 23 should 'list parent and children when get category by id' do
24 - parent = fast_create(Category)  
25 - child_1 = fast_create(Category)  
26 - child_2 = fast_create(Category) 24 + parent = fast_create(Category, :environment_id => environment.id)
  25 + child_1 = fast_create(Category, :environment_id => environment.id)
  26 + child_2 = fast_create(Category, :environment_id => environment.id)
27 27
28 - category = fast_create(Category) 28 + category = fast_create(Category, :environment_id => environment.id)
29 category.parent = parent 29 category.parent = parent
30 category.children << child_1 30 category.children << child_1
31 category.children << child_2 31 category.children << child_2
@@ -33,16 +33,16 @@ class CategoriesTest &lt; ActiveSupport::TestCase @@ -33,16 +33,16 @@ class CategoriesTest &lt; ActiveSupport::TestCase
33 33
34 get "/api/v1/categories/#{category.id}/?#{params.to_query}" 34 get "/api/v1/categories/#{category.id}/?#{params.to_query}"
35 json = JSON.parse(last_response.body) 35 json = JSON.parse(last_response.body)
36 - assert_equal({'id' => parent.id, 'name' => parent.name}, json['category']['parent']) 36 + assert_equal({'id' => parent.id, 'name' => parent.name, 'slug' => parent.slug}, json['category']['parent'])
37 assert_equivalent [child_1.id, child_2.id], json['category']['children'].map { |c| c['id'] } 37 assert_equivalent [child_1.id, child_2.id], json['category']['children'].map { |c| c['id'] }
38 end 38 end
39 39
40 should 'include parent in categories list if params is true' do 40 should 'include parent in categories list if params is true' do
41 - parent_1 = fast_create(Category) # parent_1 has no parent category  
42 - child_1 = fast_create(Category)  
43 - child_2 = fast_create(Category) 41 + parent_1 = fast_create(Category, :environment_id => environment.id) # parent_1 has no parent category
  42 + child_1 = fast_create(Category, :environment_id => environment.id)
  43 + child_2 = fast_create(Category, :environment_id => environment.id)
44 44
45 - parent_2 = fast_create(Category) 45 + parent_2 = fast_create(Category, :environment_id => environment.id)
46 parent_2.parent = parent_1 46 parent_2.parent = parent_1
47 parent_2.children << child_1 47 parent_2.children << child_1
48 parent_2.children << child_2 48 parent_2.children << child_2
@@ -60,10 +60,10 @@ class CategoriesTest &lt; ActiveSupport::TestCase @@ -60,10 +60,10 @@ class CategoriesTest &lt; ActiveSupport::TestCase
60 end 60 end
61 61
62 should 'include children in categories list if params is true' do 62 should 'include children in categories list if params is true' do
63 - category = fast_create(Category)  
64 - child_1 = fast_create(Category)  
65 - child_2 = fast_create(Category)  
66 - child_3 = fast_create(Category) 63 + category = fast_create(Category, :environment_id => environment.id)
  64 + child_1 = fast_create(Category, :environment_id => environment.id)
  65 + child_2 = fast_create(Category, :environment_id => environment.id)
  66 + child_3 = fast_create(Category, :environment_id => environment.id)
67 67
68 category.children << child_1 68 category.children << child_1
69 category.children << child_2 69 category.children << child_2
@@ -83,4 +83,15 @@ class CategoriesTest &lt; ActiveSupport::TestCase @@ -83,4 +83,15 @@ class CategoriesTest &lt; ActiveSupport::TestCase
83 json["categories"].map{ |c| c['children'].map{ |child| child['id'] }.sort } 83 json["categories"].map{ |c| c['children'].map{ |child| child['id'] }.sort }
84 end 84 end
85 85
  86 + expose_attributes = %w(id name full_name image display_color)
  87 +
  88 + expose_attributes.each do |attr|
  89 + should "expose category #{attr} attribute by default" do
  90 + category = fast_create(Category, :environment_id => environment.id)
  91 + get "/api/v1/categories/?#{params.to_query}"
  92 + json = JSON.parse(last_response.body)
  93 + assert json["categories"].last.has_key?(attr)
  94 + end
  95 + end
  96 +
86 end 97 end
test/unit/api/comments_test.rb
@@ -65,4 +65,17 @@ class CommentsTest &lt; ActiveSupport::TestCase @@ -65,4 +65,17 @@ class CommentsTest &lt; ActiveSupport::TestCase
65 assert_equal 201, last_response.status 65 assert_equal 201, last_response.status
66 assert_equal body, json['comment']['body'] 66 assert_equal body, json['comment']['body']
67 end 67 end
  68 +
  69 + should 'comment creation define the source' do
  70 + amount = Comment.count
  71 + article = fast_create(Article, :profile_id => user.person.id, :name => "Some thing")
  72 + body = 'My comment'
  73 + params.merge!({:body => body})
  74 +
  75 + post "/api/v1/articles/#{article.id}/comments?#{params.to_query}"
  76 + assert_equal amount + 1, Comment.count
  77 + comment = Comment.last
  78 + assert_not_nil comment.source
  79 + end
  80 +
68 end 81 end
test/unit/api/communities_test.rb
@@ -8,8 +8,8 @@ class CommunitiesTest &lt; ActiveSupport::TestCase @@ -8,8 +8,8 @@ class CommunitiesTest &lt; ActiveSupport::TestCase
8 end 8 end
9 9
10 should 'list only communities' do 10 should 'list only communities' do
11 - community = fast_create(Community)  
12 - enterprise = fast_create(Enterprise) # should not list this enterprise 11 + community = fast_create(Community, :environment_id => environment.id)
  12 + enterprise = fast_create(Enterprise, :environment_id => environment.id) # should not list this enterprise
13 get "/api/v1/communities?#{params.to_query}" 13 get "/api/v1/communities?#{params.to_query}"
14 json = JSON.parse(last_response.body) 14 json = JSON.parse(last_response.body)
15 assert_not_includes json['communities'].map {|c| c['id']}, enterprise.id 15 assert_not_includes json['communities'].map {|c| c['id']}, enterprise.id
@@ -17,16 +17,16 @@ class CommunitiesTest &lt; ActiveSupport::TestCase @@ -17,16 +17,16 @@ class CommunitiesTest &lt; ActiveSupport::TestCase
17 end 17 end
18 18
19 should 'list all communities' do 19 should 'list all communities' do
20 - community1 = fast_create(Community, :public_profile => true)  
21 - community2 = fast_create(Community) 20 + community1 = fast_create(Community, :environment_id => environment.id, :public_profile => true)
  21 + community2 = fast_create(Community, :environment_id => environment.id)
22 get "/api/v1/communities?#{params.to_query}" 22 get "/api/v1/communities?#{params.to_query}"
23 json = JSON.parse(last_response.body) 23 json = JSON.parse(last_response.body)
24 assert_equivalent [community1.id, community2.id], json['communities'].map {|c| c['id']} 24 assert_equivalent [community1.id, community2.id], json['communities'].map {|c| c['id']}
25 end 25 end
26 26
27 should 'not list invisible communities' do 27 should 'not list invisible communities' do
28 - community1 = fast_create(Community)  
29 - fast_create(Community, :visible => false) 28 + community1 = fast_create(Community, :environment_id => environment.id)
  29 + fast_create(Community, :environment_id => environment.id, :visible => false)
30 30
31 get "/api/v1/communities?#{params.to_query}" 31 get "/api/v1/communities?#{params.to_query}"
32 json = JSON.parse(last_response.body) 32 json = JSON.parse(last_response.body)
@@ -34,8 +34,8 @@ class CommunitiesTest &lt; ActiveSupport::TestCase @@ -34,8 +34,8 @@ class CommunitiesTest &lt; ActiveSupport::TestCase
34 end 34 end
35 35
36 should 'not list private communities without permission' do 36 should 'not list private communities without permission' do
37 - community1 = fast_create(Community)  
38 - fast_create(Community, :public_profile => false) 37 + community1 = fast_create(Community, :environment_id => environment.id)
  38 + fast_create(Community, :environment_id => environment.id, :public_profile => false)
39 39
40 get "/api/v1/communities?#{params.to_query}" 40 get "/api/v1/communities?#{params.to_query}"
41 json = JSON.parse(last_response.body) 41 json = JSON.parse(last_response.body)
@@ -43,8 +43,8 @@ class CommunitiesTest &lt; ActiveSupport::TestCase @@ -43,8 +43,8 @@ class CommunitiesTest &lt; ActiveSupport::TestCase
43 end 43 end
44 44
45 should 'list private community for members' do 45 should 'list private community for members' do
46 - c1 = fast_create(Community)  
47 - c2 = fast_create(Community, :public_profile => false) 46 + c1 = fast_create(Community, :environment_id => environment.id)
  47 + c2 = fast_create(Community, :environment_id => environment.id, :public_profile => false)
48 c2.add_member(person) 48 c2.add_member(person)
49 49
50 get "/api/v1/communities?#{params.to_query}" 50 get "/api/v1/communities?#{params.to_query}"
@@ -66,7 +66,7 @@ class CommunitiesTest &lt; ActiveSupport::TestCase @@ -66,7 +66,7 @@ class CommunitiesTest &lt; ActiveSupport::TestCase
66 end 66 end
67 67
68 should 'get community' do 68 should 'get community' do
69 - community = fast_create(Community) 69 + community = fast_create(Community, :environment_id => environment.id)
70 70
71 get "/api/v1/communities/#{community.id}?#{params.to_query}" 71 get "/api/v1/communities/#{community.id}?#{params.to_query}"
72 json = JSON.parse(last_response.body) 72 json = JSON.parse(last_response.body)
@@ -74,7 +74,7 @@ class CommunitiesTest &lt; ActiveSupport::TestCase @@ -74,7 +74,7 @@ class CommunitiesTest &lt; ActiveSupport::TestCase
74 end 74 end
75 75
76 should 'not get invisible community' do 76 should 'not get invisible community' do
77 - community = fast_create(Community, :visible => false) 77 + community = fast_create(Community, :environment_id => environment.id, :visible => false)
78 78
79 get "/api/v1/communities/#{community.id}?#{params.to_query}" 79 get "/api/v1/communities/#{community.id}?#{params.to_query}"
80 json = JSON.parse(last_response.body) 80 json = JSON.parse(last_response.body)
@@ -82,8 +82,8 @@ class CommunitiesTest &lt; ActiveSupport::TestCase @@ -82,8 +82,8 @@ class CommunitiesTest &lt; ActiveSupport::TestCase
82 end 82 end
83 83
84 should 'not get private communities without permission' do 84 should 'not get private communities without permission' do
85 - community = fast_create(Community)  
86 - fast_create(Community, :public_profile => false) 85 + community = fast_create(Community, :environment_id => environment.id)
  86 + fast_create(Community, :environment_id => environment.id, :public_profile => false)
87 87
88 get "/api/v1/communities/#{community.id}?#{params.to_query}" 88 get "/api/v1/communities/#{community.id}?#{params.to_query}"
89 json = JSON.parse(last_response.body) 89 json = JSON.parse(last_response.body)
@@ -91,17 +91,18 @@ class CommunitiesTest &lt; ActiveSupport::TestCase @@ -91,17 +91,18 @@ class CommunitiesTest &lt; ActiveSupport::TestCase
91 end 91 end
92 92
93 should 'get private community for members' do 93 should 'get private community for members' do
94 - community = fast_create(Community, :public_profile => false) 94 + community = fast_create(Community, :environment_id => environment.id, :public_profile => false, :visible => true)
95 community.add_member(person) 95 community.add_member(person)
96 96
  97 +
97 get "/api/v1/communities/#{community.id}?#{params.to_query}" 98 get "/api/v1/communities/#{community.id}?#{params.to_query}"
98 json = JSON.parse(last_response.body) 99 json = JSON.parse(last_response.body)
99 assert_equal community.id, json['community']['id'] 100 assert_equal community.id, json['community']['id']
100 end 101 end
101 102
102 should 'list person communities' do 103 should 'list person communities' do
103 - community = fast_create(Community)  
104 - fast_create(Community) 104 + community = fast_create(Community, :environment_id => environment.id)
  105 + fast_create(Community, :environment_id => environment.id)
105 community.add_member(person) 106 community.add_member(person)
106 107
107 get "/api/v1/people/#{person.id}/communities?#{params.to_query}" 108 get "/api/v1/people/#{person.id}/communities?#{params.to_query}"
@@ -110,8 +111,8 @@ class CommunitiesTest &lt; ActiveSupport::TestCase @@ -110,8 +111,8 @@ class CommunitiesTest &lt; ActiveSupport::TestCase
110 end 111 end
111 112
112 should 'not list person communities invisible' do 113 should 'not list person communities invisible' do
113 - c1 = fast_create(Community)  
114 - c2 = fast_create(Community, :visible => false) 114 + c1 = fast_create(Community, :environment_id => environment.id)
  115 + c2 = fast_create(Community, :environment_id => environment.id, :visible => false)
115 c1.add_member(person) 116 c1.add_member(person)
116 c2.add_member(person) 117 c2.add_member(person)
117 118
test/unit/api/enterprises_test.rb
@@ -8,8 +8,8 @@ class EnterprisesTest &lt; ActiveSupport::TestCase @@ -8,8 +8,8 @@ class EnterprisesTest &lt; ActiveSupport::TestCase
8 end 8 end
9 9
10 should 'list only enterprises' do 10 should 'list only enterprises' do
11 - community = fast_create(Community) # should not list this community  
12 - enterprise = fast_create(Enterprise, :public_profile => true) 11 + community = fast_create(Community, :environment_id => environment.id) # should not list this community
  12 + enterprise = fast_create(Enterprise, :environment_id => environment.id, :public_profile => true)
13 get "/api/v1/enterprises?#{params.to_query}" 13 get "/api/v1/enterprises?#{params.to_query}"
14 json = JSON.parse(last_response.body) 14 json = JSON.parse(last_response.body)
15 assert_includes json['enterprises'].map {|c| c['id']}, enterprise.id 15 assert_includes json['enterprises'].map {|c| c['id']}, enterprise.id
@@ -17,15 +17,15 @@ class EnterprisesTest &lt; ActiveSupport::TestCase @@ -17,15 +17,15 @@ class EnterprisesTest &lt; ActiveSupport::TestCase
17 end 17 end
18 18
19 should 'list all enterprises' do 19 should 'list all enterprises' do
20 - enterprise1 = fast_create(Enterprise, :public_profile => true)  
21 - enterprise2 = fast_create(Enterprise) 20 + enterprise1 = fast_create(Enterprise, :environment_id => environment.id, :public_profile => true)
  21 + enterprise2 = fast_create(Enterprise, :environment_id => environment.id)
22 get "/api/v1/enterprises?#{params.to_query}" 22 get "/api/v1/enterprises?#{params.to_query}"
23 json = JSON.parse(last_response.body) 23 json = JSON.parse(last_response.body)
24 assert_equivalent [enterprise1.id, enterprise2.id], json['enterprises'].map {|c| c['id']} 24 assert_equivalent [enterprise1.id, enterprise2.id], json['enterprises'].map {|c| c['id']}
25 end 25 end
26 26
27 should 'not list invisible enterprises' do 27 should 'not list invisible enterprises' do
28 - enterprise1 = fast_create(Enterprise) 28 + enterprise1 = fast_create(Enterprise, :environment_id => environment.id)
29 fast_create(Enterprise, :visible => false) 29 fast_create(Enterprise, :visible => false)
30 30
31 get "/api/v1/enterprises?#{params.to_query}" 31 get "/api/v1/enterprises?#{params.to_query}"
@@ -34,8 +34,8 @@ class EnterprisesTest &lt; ActiveSupport::TestCase @@ -34,8 +34,8 @@ class EnterprisesTest &lt; ActiveSupport::TestCase
34 end 34 end
35 35
36 should 'not list private enterprises without permission' do 36 should 'not list private enterprises without permission' do
37 - enterprise1 = fast_create(Enterprise)  
38 - fast_create(Enterprise, :public_profile => false) 37 + enterprise1 = fast_create(Enterprise, :environment_id => environment.id)
  38 + fast_create(Enterprise, :environment_id => environment.id, :public_profile => false)
39 39
40 get "/api/v1/enterprises?#{params.to_query}" 40 get "/api/v1/enterprises?#{params.to_query}"
41 json = JSON.parse(last_response.body) 41 json = JSON.parse(last_response.body)
@@ -43,8 +43,8 @@ class EnterprisesTest &lt; ActiveSupport::TestCase @@ -43,8 +43,8 @@ class EnterprisesTest &lt; ActiveSupport::TestCase
43 end 43 end
44 44
45 should 'list private enterprise for members' do 45 should 'list private enterprise for members' do
46 - c1 = fast_create(Enterprise)  
47 - c2 = fast_create(Enterprise, :public_profile => false) 46 + c1 = fast_create(Enterprise, :environment_id => environment.id)
  47 + c2 = fast_create(Enterprise, :environment_id => environment.id, :public_profile => false)
48 c2.add_member(person) 48 c2.add_member(person)
49 49
50 get "/api/v1/enterprises?#{params.to_query}" 50 get "/api/v1/enterprises?#{params.to_query}"
@@ -53,7 +53,7 @@ class EnterprisesTest &lt; ActiveSupport::TestCase @@ -53,7 +53,7 @@ class EnterprisesTest &lt; ActiveSupport::TestCase
53 end 53 end
54 54
55 should 'get enterprise' do 55 should 'get enterprise' do
56 - enterprise = fast_create(Enterprise) 56 + enterprise = fast_create(Enterprise, :environment_id => environment.id)
57 57
58 get "/api/v1/enterprises/#{enterprise.id}?#{params.to_query}" 58 get "/api/v1/enterprises/#{enterprise.id}?#{params.to_query}"
59 json = JSON.parse(last_response.body) 59 json = JSON.parse(last_response.body)
@@ -69,8 +69,8 @@ class EnterprisesTest &lt; ActiveSupport::TestCase @@ -69,8 +69,8 @@ class EnterprisesTest &lt; ActiveSupport::TestCase
69 end 69 end
70 70
71 should 'not get private enterprises without permission' do 71 should 'not get private enterprises without permission' do
72 - enterprise = fast_create(Enterprise)  
73 - fast_create(Enterprise, :public_profile => false) 72 + enterprise = fast_create(Enterprise, :environment_id => environment.id)
  73 + fast_create(Enterprise, :environment_id => environment.id, :public_profile => false)
74 74
75 get "/api/v1/enterprises/#{enterprise.id}?#{params.to_query}" 75 get "/api/v1/enterprises/#{enterprise.id}?#{params.to_query}"
76 json = JSON.parse(last_response.body) 76 json = JSON.parse(last_response.body)
@@ -87,8 +87,8 @@ class EnterprisesTest &lt; ActiveSupport::TestCase @@ -87,8 +87,8 @@ class EnterprisesTest &lt; ActiveSupport::TestCase
87 end 87 end
88 88
89 should 'list person enterprises' do 89 should 'list person enterprises' do
90 - enterprise = fast_create(Enterprise)  
91 - fast_create(Enterprise) 90 + enterprise = fast_create(Enterprise, :environment_id => environment.id)
  91 + fast_create(Enterprise, :environment_id => environment.id)
92 enterprise.add_member(person) 92 enterprise.add_member(person)
93 93
94 get "/api/v1/people/#{person.id}/enterprises?#{params.to_query}" 94 get "/api/v1/people/#{person.id}/enterprises?#{params.to_query}"
@@ -97,8 +97,8 @@ class EnterprisesTest &lt; ActiveSupport::TestCase @@ -97,8 +97,8 @@ class EnterprisesTest &lt; ActiveSupport::TestCase
97 end 97 end
98 98
99 should 'not list person enterprises invisible' do 99 should 'not list person enterprises invisible' do
100 - c1 = fast_create(Enterprise)  
101 - c2 = fast_create(Enterprise, :visible => false) 100 + c1 = fast_create(Enterprise, :environment_id => environment.id)
  101 + c2 = fast_create(Enterprise, :environment_id => environment.id, :visible => false)
102 c1.add_member(person) 102 c1.add_member(person)
103 c2.add_member(person) 103 c2.add_member(person)
104 104
test/unit/api/helpers_test.rb
@@ -25,15 +25,6 @@ class APIHelpersTest &lt; ActiveSupport::TestCase @@ -25,15 +25,6 @@ class APIHelpersTest &lt; ActiveSupport::TestCase
25 assert_equal user, current_user 25 assert_equal user, current_user
26 end 26 end
27 27
28 - should 'not get the current user with expired token' do  
29 - user = create_user('someuser')  
30 - user.generate_private_token!  
31 - user.private_token_generated_at = DateTime.now.prev_year  
32 - user.save  
33 - self.params = {:private_token => user.private_token}  
34 - assert_nil current_user  
35 - end  
36 -  
37 should 'get the person of current user' do 28 should 'get the person of current user' do
38 user = create_user('someuser') 29 user = create_user('someuser')
39 user.generate_private_token! 30 user.generate_private_token!
@@ -161,6 +152,39 @@ class APIHelpersTest &lt; ActiveSupport::TestCase @@ -161,6 +152,39 @@ class APIHelpersTest &lt; ActiveSupport::TestCase
161 assert_nil make_conditions_with_parameter[:type] 152 assert_nil make_conditions_with_parameter[:type]
162 end 153 end
163 154
  155 + #test_should_make_order_with_parameters_return_order_if attribute_is_found_at_object_association
  156 + should 'make_order_with_parameters return order if attribute is found at object association' do
  157 + environment = Environment.new
  158 + params = {:order => "name ASC"}
  159 + assert_equal "name ASC", make_order_with_parameters(environment, "articles", params)
  160 + end
  161 +
  162 + # test added to check for eventual sql injection vunerabillity
  163 + #test_should_make_order_with_parameters_return_default_order_if_attributes_not_exists
  164 + should 'make_order_with_parameters return default order if attributes not exists' do
  165 + environment = Environment.new
  166 + params = {:order => "CRAZY_FIELD ASC"} # quote used to check sql injection vunerabillity
  167 + assert_equal "created_at DESC", make_order_with_parameters(environment, "articles", params)
  168 + end
  169 +
  170 + should 'make_order_with_parameters return default order if sql injection detected' do
  171 + environment = Environment.new
  172 + params = {:order => "name' ASC"} # quote used to check sql injection vunerabillity
  173 + assert_equal "created_at DESC", make_order_with_parameters(environment, "articles", params)
  174 + end
  175 +
  176 + should 'make_order_with_parameters return RANDOM() if random is passed' do
  177 + environment = Environment.new
  178 + params = {:order => "random"} # quote used to check sql injection vunerabillity
  179 + assert_equal "RANDOM()", make_order_with_parameters(environment, "articles", params)
  180 + end
  181 +
  182 + should 'make_order_with_parameters return RANDOM() if random function is passed' do
  183 + environment = Environment.new
  184 + params = {:order => "random()"} # quote used to check sql injection vunerabillity
  185 + assert_equal "RANDOM()", make_order_with_parameters(environment, "articles", params)
  186 + end
  187 +
164 should 'render not_found if endpoint is unavailable' do 188 should 'render not_found if endpoint is unavailable' do
165 Noosfero::API::API.stubs(:endpoint_unavailable?).returns(true) 189 Noosfero::API::API.stubs(:endpoint_unavailable?).returns(true)
166 self.expects(:not_found!) 190 self.expects(:not_found!)
test/unit/api/people_test.rb
@@ -148,4 +148,21 @@ class PeopleTest &lt; ActiveSupport::TestCase @@ -148,4 +148,21 @@ class PeopleTest &lt; ActiveSupport::TestCase
148 get "/api/v1/people/#{some_person.id}/permissions?#{params.to_query}" 148 get "/api/v1/people/#{some_person.id}/permissions?#{params.to_query}"
149 assert_equal 403, last_response.status 149 assert_equal 403, last_response.status
150 end 150 end
  151 +
  152 + should 'not update another person' do
  153 + person = fast_create(Person, :environment_id => environment.id)
  154 + post "/api/v1/people/#{person.id}?#{params.to_query}"
  155 + assert_equal 403, last_response.status
  156 + end
  157 +
  158 + should 'update yourself' do
  159 + another_name = 'Another Name'
  160 + params[:person] = {}
  161 + params[:person][:name] = another_name
  162 + assert_not_equal another_name, person.name
  163 + post "/api/v1/people/#{person.id}?#{params.to_query}"
  164 + person.reload
  165 + assert_equal another_name, person.name
  166 + end
  167 +
151 end 168 end
test/unit/api/search_test.rb 0 → 100644
@@ -0,0 +1,138 @@ @@ -0,0 +1,138 @@
  1 +require File.dirname(__FILE__) + '/test_helper'
  2 +
  3 +class SearchTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @person = create_user('testing').person
  7 + end
  8 + attr_reader :person
  9 +
  10 + should 'not list unpublished articles' do
  11 + Article.delete_all
  12 + article = fast_create(Article, :profile_id => person.id, :published => false)
  13 + assert !article.published?
  14 + get "/api/v1/search/article"
  15 + json = JSON.parse(last_response.body)
  16 + assert_empty json['articles']
  17 + end
  18 +
  19 + should 'list articles' do
  20 + fast_create(Article, :profile_id => person.id)
  21 + get "/api/v1/search/article"
  22 + json = JSON.parse(last_response.body)
  23 + assert_not_empty json['articles']
  24 + end
  25 +
  26 + should 'invalid search string articles' do
  27 + fast_create(Article, :profile_id => person.id, :name => 'some article')
  28 + get "/api/v1/search/article?query=test"
  29 + json = JSON.parse(last_response.body)
  30 + assert_empty json['articles']
  31 + end
  32 +
  33 + should 'do not list articles of wrong type' do
  34 + fast_create(Article, :profile_id => person.id)
  35 + get "/api/v1/search/article?type=TinyMceArticle"
  36 + json = JSON.parse(last_response.body)
  37 + assert_empty json['articles']
  38 + end
  39 +
  40 + should 'list articles of one type' do
  41 + fast_create(Article, :profile_id => person.id)
  42 + article = fast_create(TinyMceArticle, :profile_id => person.id)
  43 +
  44 + get "/api/v1/search/article?type=TinyMceArticle"
  45 + json = JSON.parse(last_response.body)
  46 + assert_equal article.id, json['articles'].first['id']
  47 + end
  48 +
  49 + should 'list articles of one type and query string' do
  50 + fast_create(Article, :profile_id => person.id, :name => 'some article')
  51 + fast_create(Article, :profile_id => person.id, :name => 'Some thing')
  52 + article = fast_create(TinyMceArticle, :profile_id => person.id, :name => 'Some thing')
  53 + get "/api/v1/search/article?type=TinyMceArticle&query=thing"
  54 + json = JSON.parse(last_response.body)
  55 + assert_equal 1, json['articles'].count
  56 + assert_equal article.id, json['articles'].first['id']
  57 + end
  58 +
  59 + should 'not return more entries than page limit' do
  60 + 1.upto(5).each do |n|
  61 + fast_create(Article, :profile_id => person.id, :name => "Article #{n}")
  62 + end
  63 +
  64 + get "/api/v1/search/article?query=Article&per_page=3"
  65 + json = JSON.parse(last_response.body)
  66 +
  67 + assert_equal 3, json['articles'].count
  68 + end
  69 +
  70 + should 'return entries second page' do
  71 + 1.upto(5).each do |n|
  72 + fast_create(Article, :profile_id => person.id, :name => "Article #{n}")
  73 + end
  74 +
  75 + get "/api/v1/search/article?query=Article&per_page=3&page=2"
  76 + json = JSON.parse(last_response.body)
  77 +
  78 + assert_equal 2, json['articles'].count
  79 + end
  80 +
  81 + should 'search articles in profile' do
  82 + person2 = fast_create(Person)
  83 + fast_create(Article, :profile_id => person.id)
  84 + fast_create(Article, :profile_id => person.id)
  85 + article = fast_create(Article, :profile_id => person2.id)
  86 +
  87 + get "/api/v1/search/article?query=Article&profile_id=#{person2.id}"
  88 + json = JSON.parse(last_response.body)
  89 + assert_equal article.id, json['articles'].first['id']
  90 + end
  91 +
  92 + should 'search and return values specified in fields parameter' do
  93 + fast_create(Article, :profile_id => person.id)
  94 + get "/api/v1/search/article?fields=title"
  95 + json = JSON.parse(last_response.body)
  96 + assert_not_empty json['articles']
  97 + assert_equal ['title'], json['articles'].first.keys
  98 + end
  99 +
  100 + should 'search with parent' do
  101 + parent = fast_create(Folder, :profile_id => person.id)
  102 + fast_create(Article, :profile_id => person.id)
  103 + article = fast_create(Article, :profile_id => person.id, :parent_id => parent.id)
  104 + get "/api/v1/search/article?parent_id=#{parent.id}"
  105 + json = JSON.parse(last_response.body)
  106 + assert_equal 1, json['articles'].count
  107 + assert_equal article.id, json['articles'].first["id"]
  108 + end
  109 +
  110 + should 'search filter by category' do
  111 + Article.delete_all
  112 + fast_create(Article, :profile_id => person.id)
  113 + article = fast_create(Article, :profile_id => person.id)
  114 + category = fast_create(Category)
  115 + article.categories<< category
  116 + get "/api/v1/search/article?category_ids=#{category.id}"
  117 + json = JSON.parse(last_response.body)
  118 + assert_equal 1, json['articles'].count
  119 + assert_equal article.id, json['articles'].first["id"]
  120 + end
  121 +
  122 + should 'search filter by more than one category' do
  123 + Article.delete_all
  124 + fast_create(Article, :profile_id => person.id)
  125 + article1 = fast_create(Article, :profile_id => person.id)
  126 + article2 = fast_create(Article, :profile_id => person.id)
  127 + category1 = fast_create(Category)
  128 + category2 = fast_create(Category)
  129 + article1.categories<< category1
  130 + article2.categories<< category2
  131 + get "/api/v1/search/article?category_ids[]=#{category1.id}&category_ids[]=#{category2.id}"
  132 + json = JSON.parse(last_response.body)
  133 + assert_equal 2, json['articles'].count
  134 + assert_equal article1.id, json['articles'].first["id"]
  135 + assert_equal article2.id, json['articles'].last["id"]
  136 + end
  137 +
  138 +end
test/unit/api/session_test.rb
@@ -10,7 +10,7 @@ class SessionTest &lt; ActiveSupport::TestCase @@ -10,7 +10,7 @@ class SessionTest &lt; ActiveSupport::TestCase
10 params = {:login => "testapi", :password => "testapi"} 10 params = {:login => "testapi", :password => "testapi"}
11 post "/api/v1/login?#{params.to_query}" 11 post "/api/v1/login?#{params.to_query}"
12 json = JSON.parse(last_response.body) 12 json = JSON.parse(last_response.body)
13 - assert !json["private_token"].blank? 13 + assert !json['user']["private_token"].blank?
14 end 14 end
15 15
16 should 'return 401 when login fails' do 16 should 'return 401 when login fails' do
@@ -21,22 +21,167 @@ class SessionTest &lt; ActiveSupport::TestCase @@ -21,22 +21,167 @@ class SessionTest &lt; ActiveSupport::TestCase
21 end 21 end
22 22
23 should 'register a user' do 23 should 'register a user' do
24 - params = {:login => "newuserapi", :password => "newuserapi", :email => "newuserapi@email.com" } 24 + Environment.default.enable('skip_new_user_email_confirmation')
  25 + params = {:login => "newuserapi", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com" }
25 post "/api/v1/register?#{params.to_query}" 26 post "/api/v1/register?#{params.to_query}"
26 assert_equal 201, last_response.status 27 assert_equal 201, last_response.status
  28 + json = JSON.parse(last_response.body)
  29 + assert User['newuserapi'].activated?
  30 + assert json['user']['activated']
  31 + assert json['user']['private_token'].present?
  32 + end
  33 +
  34 + should 'register a user with name' do
  35 + Environment.default.enable('skip_new_user_email_confirmation')
  36 + params = {:login => "newuserapi", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com", :name => "Little John" }
  37 + post "/api/v1/register?#{params.to_query}"
  38 + assert_equal 201, last_response.status
  39 + json = JSON.parse(last_response.body)
  40 + assert json['user']['activated']
  41 + assert json['user']['private_token'].present?
  42 + end
  43 +
  44 + should 'register an inactive user' do
  45 + params = {:login => "newuserapi", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com" }
  46 + post "/api/v1/register?#{params.to_query}"
  47 + assert_equal 201, last_response.status
  48 + json = JSON.parse(last_response.body)
  49 + assert !json['activated']
  50 + assert json['private_token'].blank?
27 end 51 end
28 52
29 - should 'do not register a user without email' do  
30 - params = {:login => "newuserapi", :password => "newuserapi", :email => nil } 53 + should 'not register a user with invalid login' do
  54 + params = {:login => "c", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com" }
31 post "/api/v1/register?#{params.to_query}" 55 post "/api/v1/register?#{params.to_query}"
32 assert_equal 400, last_response.status 56 assert_equal 400, last_response.status
  57 + json = JSON.parse(last_response.body)
  58 + msg = json['message'].split(':')
  59 + key = msg[0][2, 5]
  60 + val = msg[1][2, 38]
  61 + assert_equal "login", key
  62 + assert_equal "is too short (minimum is 2 characters)", val
33 end 63 end
34 64
35 - should 'do not register a duplicated user' do  
36 - params = {:login => "newuserapi", :password => "newuserapi", :email => "newuserapi@email.com" } 65 + should 'not register a user with invalid login pt' do
  66 + I18n.locale = "pt-BR"
  67 + params = {:lang => "pt-BR", :login => "c", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com" }
37 post "/api/v1/register?#{params.to_query}" 68 post "/api/v1/register?#{params.to_query}"
  69 + assert_equal 400, last_response.status
  70 + json = JSON.parse(last_response.body)
  71 + msg = json['message'].split(':')
  72 + key = msg[0][2, 5]
  73 + val = msg[1][2, 35]
  74 + assert_equal "login", key
  75 + assert val.include? "muito curto"
  76 + end
  77 +
  78 + should 'not register a user without email' do
  79 + params = {:login => "newuserapi", :password => "newuserapi", :password_confirmation => "newuserapi", :email => nil }
38 post "/api/v1/register?#{params.to_query}" 80 post "/api/v1/register?#{params.to_query}"
39 assert_equal 400, last_response.status 81 assert_equal 400, last_response.status
40 end 82 end
41 83
  84 + should 'not register a duplicated user' do
  85 + params = {:login => "newuserapi", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com" }
  86 + post "/api/v1/register?#{params.to_query}"
  87 + post "/api/v1/register?#{params.to_query}"
  88 + assert_equal 400, last_response.status
  89 + json = JSON.parse(last_response.body)
  90 + end
  91 +
  92 + # TODO: Add another test cases to check register situations
  93 + should 'activate a user' do
  94 + params = {
  95 + :login => "newuserapi",
  96 + :password => "newuserapi",
  97 + :password_confirmation => "newuserapi",
  98 + :email => "newuserapi@email.com"
  99 + }
  100 + user = User.new(params)
  101 + user.save!
  102 +
  103 + params = { activation_code: user.activation_code}
  104 + patch "/api/v1/activate?#{params.to_query}"
  105 + assert_equal 200, last_response.status
  106 + end
  107 +
  108 + should 'do not activate a user if admin must approve him' do
  109 + params = {
  110 + :login => "newuserapi",
  111 + :password => "newuserapi",
  112 + :password_confirmation => "newuserapi",
  113 + :email => "newuserapi@email.com",
  114 + :environment => Environment.default
  115 + }
  116 + user = User.new(params)
  117 + user.environment.enable('admin_must_approve_new_users')
  118 + user.save!
  119 +
  120 + params = { activation_code: user.activation_code}
  121 + patch "/api/v1/activate?#{params.to_query}"
  122 + assert_equal 202, last_response.status
  123 + assert_equal 'Waiting for admin moderate user registration', JSON.parse(last_response.body)["message"]
  124 + end
  125 +
  126 + should 'do not activate a user if the token is invalid' do
  127 + params = {
  128 + :login => "newuserapi",
  129 + :password => "newuserapi",
  130 + :password_confirmation => "newuserapi",
  131 + :email => "newuserapi@email.com",
  132 + :environment => Environment.default
  133 + }
  134 + user = User.new(params)
  135 + user.save!
  136 +
  137 + params = { activation_code: '70250abe20cc6a67ef9399cf3286cb998b96aeaf'}
  138 + patch "/api/v1/activate?#{params.to_query}"
  139 + assert_equal 412, last_response.status
  140 + end
  141 +
  142 + should 'create task to change password by user login' do
  143 + user = create_user
  144 + params = {:value => user.login}
  145 + assert_difference 'ChangePassword.count' do
  146 + post "/api/v1/forgot_password?#{params.to_query}"
  147 + end
  148 + end
  149 +
  150 + should 'not create task to change password when user is not found' do
  151 + params = {:value => 'wronglogin'}
  152 + assert_no_difference 'ChangePassword.count' do
  153 + post "/api/v1/forgot_password?#{params.to_query}"
  154 + end
  155 + assert_equal 404, last_response.status
  156 + end
  157 +
  158 + should 'change user password and close task' do
  159 + user = create_user
  160 + user.activate
  161 + task = ChangePassword.create!(:requestor => user.person)
  162 + params = {:code => task.code, :password => 'secret', :password_confirmation => 'secret'}
  163 + patch "/api/v1/new_password?#{params.to_query}"
  164 + assert_equal Task::Status::FINISHED, task.reload.status
  165 + assert user.reload.authenticated?('secret')
  166 + json = JSON.parse(last_response.body)
  167 + assert_equal user.id, json['user']['id']
  168 + end
  169 +
  170 + should 'do not change user password when password confirmation is wrong' do
  171 + user = create_user
  172 + user.activate
  173 + task = ChangePassword.create!(:requestor => user.person)
  174 + params = {:code => task.code, :password => 'secret', :password_confirmation => 's3cret'}
  175 + patch "/api/v1/new_password?#{params.to_query}"
  176 + assert_equal Task::Status::ACTIVE, task.reload.status
  177 + assert !user.reload.authenticated?('secret')
  178 + assert_equal 400, last_response.status
  179 + end
  180 +
  181 + should 'render not found when provide a wrong code on password change' do
  182 + params = {:code => "wrongcode", :password => 'secret', :password_confirmation => 'secret'}
  183 + patch "/api/v1/new_password?#{params.to_query}"
  184 + assert_equal 404, last_response.status
  185 + end
  186 +
42 end 187 end
test/unit/api/test_helper.rb
@@ -9,16 +9,22 @@ class ActiveSupport::TestCase @@ -9,16 +9,22 @@ class ActiveSupport::TestCase
9 end 9 end
10 10
11 def login_api 11 def login_api
12 - @user = User.create!(:login => 'testapi', :password => 'testapi', :password_confirmation => 'testapi', :email => 'test@test.org', :environment => Environment.default) 12 + @environment = Environment.default
  13 + @user = User.create!(:login => 'testapi', :password => 'testapi', :password_confirmation => 'testapi', :email => 'test@test.org', :environment => @environment)
13 @user.activate 14 @user.activate
14 @person = @user.person 15 @person = @user.person
15 16
16 post "/api/v1/login?login=testapi&password=testapi" 17 post "/api/v1/login?login=testapi&password=testapi"
17 json = JSON.parse(last_response.body) 18 json = JSON.parse(last_response.body)
18 @private_token = json["private_token"] 19 @private_token = json["private_token"]
  20 + unless @private_token
  21 + @user.generate_private_token!
  22 + @private_token = @user.private_token
  23 + end
  24 +
19 @params = {:private_token => @private_token} 25 @params = {:private_token => @private_token}
20 end 26 end
21 - attr_accessor :private_token, :user, :person, :params 27 + attr_accessor :private_token, :user, :person, :params, :environment
22 28
23 private 29 private
24 30
test/unit/api/users_test.rb 0 → 100644
@@ -0,0 +1,62 @@ @@ -0,0 +1,62 @@
  1 +# encoding: UTF-8
  2 +require File.dirname(__FILE__) + '/test_helper'
  3 +
  4 +class UsersTest < ActiveSupport::TestCase
  5 +
  6 + def setup
  7 + login_api
  8 + end
  9 +
  10 + should 'list users' do
  11 + get "/api/v1/users/?#{params.to_query}"
  12 + json = JSON.parse(last_response.body)
  13 + assert_includes json["users"].map { |a| a["login"] }, user.login
  14 + end
  15 +
  16 + should 'create a user' do
  17 + params[:user] = {:login => 'some', :password => '123456', :password_confirmation => '123456', :email => 'some@some.com'}
  18 + post "/api/v1/users?#{params.to_query}"
  19 + json = JSON.parse(last_response.body)
  20 + assert_equal 'some', json['user']['login']
  21 + end
  22 +
  23 + should 'not create duplicate user' do
  24 + params[:lang] = :"pt-BR"
  25 + params[:user] = {:login => 'some', :password => '123456', :password_confirmation => '123456', :email => 'some@some.com'}
  26 + post "/api/v1/users?#{params.to_query}"
  27 + json = JSON.parse(last_response.body)
  28 + assert_equal 'some', json['user']['login']
  29 + params[:user] = {:login => 'some', :password => '123456', :password_confirmation => '123456', :email => 'some@some.com'}
  30 + post "/api/v1/users?#{params.to_query}"
  31 + json = JSON.parse(last_response.body)
  32 + assert_equal 'Username / Email já está em uso,e-Mail já está em uso', json['message']
  33 + end
  34 +
  35 + should 'return 400 status for invalid user creation' do
  36 + params[:user] = {:login => 'some'}
  37 + post "/api/v1/users?#{params.to_query}"
  38 + json = JSON.parse(last_response.body)
  39 + assert_equal 400, last_response.status
  40 + end
  41 +
  42 + should 'get user' do
  43 + get "/api/v1/users/#{user.id}?#{params.to_query}"
  44 + json = JSON.parse(last_response.body)
  45 + assert_equal user.id, json['user']['id']
  46 + end
  47 +
  48 + should 'list user permissions' do
  49 + community = fast_create(Community)
  50 + community.add_admin(person)
  51 + get "/api/v1/users/#{user.id}/?#{params.to_query}"
  52 + json = JSON.parse(last_response.body)
  53 + assert_includes json["user"]["permissions"], community.identifier
  54 + end
  55 +
  56 + should 'get logged user' do
  57 + get "/api/v1/users/me?#{params.to_query}"
  58 + json = JSON.parse(last_response.body)
  59 + assert_equal user.id, json['user']['id']
  60 + end
  61 +
  62 +end