Commit 74184b5cfa4c2605751259d3fcd3dbbb77e52bf1

Authored by Caio Almeida
2 parents 94d1fa50 b34e0fa0

fix entities api conflit

Gemfile
... ... @@ -23,6 +23,10 @@ gem 'eita-jrails', '~> 0.9.5', require: 'jrails'
23 23 # API dependencies
24 24 gem 'grape', '~> 0.12'
25 25 gem 'grape-entity'
  26 +gem 'grape-swagger'
  27 +gem 'swagger-ui_rails'
  28 +gem 'kramdown'
  29 +
26 30 #FIXME Get the Grape Loggin from master yo solve this issue https://github.com/intridea/grape/issues/1059
27 31 #We have to remove this commit referenve code when update the next release of grape_logging. Actualy we are using (1.1.2)
28 32 gem 'grape_logging', :git => 'https://github.com/aceunreal/grape_logging.git', :ref => 'f1755ae'
... ...
Gemfile.lock
... ... @@ -103,12 +103,16 @@ GEM
103 103 grape-entity (0.4.5)
104 104 activesupport
105 105 multi_json (>= 1.3.2)
  106 + grape-swagger (0.10.2)
  107 + grape (>= 0.8.0)
  108 + grape-entity
106 109 hashie (2.1.2)
107 110 hike (1.2.3)
108 111 i18n (0.7.0)
109 112 ice_nine (0.11.1)
110 113 journey (1.0.4)
111 114 json (1.8.3)
  115 + kramdown (1.8.0)
112 116 libv8 (3.16.14.11)
113 117 liquid (3.0.6)
114 118 locale (2.0.9)
... ... @@ -202,6 +206,7 @@ GEM
202 206 multi_json (~> 1.0)
203 207 rack (~> 1.0)
204 208 tilt (~> 1.1, != 1.3.0)
  209 + swagger-ui_rails (0.1.7)
205 210 term-ansicolor (1.3.2)
206 211 tins (~> 1.0)
207 212 therubyracer (0.12.2)
... ... @@ -254,7 +259,9 @@ DEPENDENCIES
254 259 gettext (~> 2.2.1)
255 260 grape (~> 0.12)
256 261 grape-entity
  262 + grape-swagger
257 263 grape_logging!
  264 + kramdown
258 265 liquid (~> 3.0.3)
259 266 locale (~> 2.0.5)
260 267 minitest (~> 3.2.0)
... ... @@ -274,6 +281,7 @@ DEPENDENCIES
274 281 rubyzip
275 282 sass-rails
276 283 selenium-webdriver (~> 2.39.0)
  284 + swagger-ui_rails
277 285 therubyracer
278 286 thin (~> 1.3.1)
279 287 uglifier (>= 1.0.3)
... ...
app/controllers/api_docs_controller.rb 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +class ApiDocsController < ApplicationController
  2 + layout :api_layout
  3 +
  4 + private
  5 + def api_layout
  6 + params[:controller]
  7 + end
  8 +end
... ...
app/controllers/public/account_controller.rb
... ... @@ -196,6 +196,11 @@ class AccountController &lt; ApplicationController
196 196  
197 197 if request.post?
198 198 begin
  199 + unless verify_recaptcha
  200 + @change_password.errors.add(:base, _('Please type the words correctly'))
  201 + return false
  202 + end
  203 +
199 204 requestors = fetch_requestors(params[:value])
200 205 raise ActiveRecord::RecordNotFound if requestors.blank? || params[:value].blank?
201 206  
... ...
app/views/account/forgot_password.html.erb
... ... @@ -5,6 +5,9 @@
5 5 <%= form_tag({:action => 'forgot_password'}, :method => 'post', :id => 'forgot-password-form') do %>
6 6 <%= labelled_form_field fields_label, text_field_tag(:value) %>
7 7  
  8 + <h3><%= _('Please type the two words below') %></h3>
  9 + <%= recaptcha_tags(:display => { :theme => 'clean' }, :ajax => true) %>
  10 +
8 11 <div>
9 12 <% button_bar do %>
10 13 <%= submit_button('send', _('Send instructions')) %>
... ...
app/views/api_docs/index.html.erb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<%= render 'swagger_ui/swagger_ui', discovery_url: '/api/v1/api_docs' %>
... ...
app/views/layouts/api_docs.html.erb 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +<!DOCTYPE html>
  2 +<html>
  3 + <head>
  4 + <meta charset="utf-8">
  5 + <title></title>
  6 + <%= stylesheet_link_tag 'swagger-ui' %>
  7 + <%= javascript_include_tag 'swagger-ui' %>
  8 + </head>
  9 + <body>
  10 + <div id="content">
  11 + <%= yield %>
  12 + </div>
  13 + </body>
  14 +</html>
... ...
config/routes.rb
... ... @@ -23,6 +23,7 @@ Noosfero::Application.routes.draw do
23 23  
24 24 match 'site(/:action)', :controller => 'home'
25 25 match 'api(/:action)', :controller => 'api'
  26 + match 'api_docs(/:action)', :controller => 'api_docs'
26 27  
27 28 match 'images(/*stuff)' => 'not_found#nothing'
28 29 match 'stylesheets(/*stuff)' => 'not_found#nothing'
... ...
lib/noosfero/api/api.rb
... ... @@ -51,9 +51,12 @@ module Noosfero
51 51 mount V1::Tasks
52 52 mount V1::Tags
53 53 mount V1::Environments
  54 + mount V1::Search
54 55  
55 56 mount Session
56 57  
  58 + add_swagger_documentation api_version: 'v1', mount_path: '/api_docs', markdown: GrapeSwagger::Markdown::KramdownAdapter unless Rails.env.production?
  59 +
57 60 # hook point which allow plugins to add Grape::API extensions to API::API
58 61 #finds for plugins which has api mount points classes defined (the class should extends Grape::API)
59 62 @plugins = Noosfero::Plugin.all.map { |p| p.constantize }
... ...
lib/noosfero/api/entities.rb
... ... @@ -67,7 +67,7 @@ module Noosfero
67 67  
68 68 class Person < Profile
69 69 root 'people', 'person'
70   - expose :user, :using => UserBasic
  70 + expose :user, :using => UserBasic, documentation: {type: 'User', desc: 'The user data of a person' }
71 71 expose :orientacao_sexual, :identidade_genero, :transgenero, :etnia
72 72 end
73 73  
... ... @@ -84,11 +84,11 @@ module Noosfero
84 84 root 'articles', 'article'
85 85 expose :id
86 86 expose :body
87   - expose :abstract
  87 + expose :abstract, documentation: {type: 'String', desc: 'Teaser of the body'}
88 88 expose :created_at, :format_with => :timestamp
89 89 expose :title, :documentation => {:type => "String", :desc => "Title of the article"}
90   - expose :created_by, :as => :author, :using => Profile
91   - expose :profile, :using => Profile
  90 + expose :created_by, :as => :author, :using => Profile, :documentation => {type: 'Profile', desc: 'The profile author that create the article'}
  91 + expose :profile, :using => Profile, :documentation => {type: 'Profile', desc: 'The profile associated with the article'}
92 92 expose :categories, :using => Category
93 93 expose :image, :using => Image
94 94 #TODO Apply vote stuff in core and make this test
... ... @@ -98,7 +98,7 @@ module Noosfero
98 98 expose :position
99 99 expose :hits
100 100 expose :start_date
101   - expose :end_date
  101 + expose :end_date, :documentation => {type: 'DateTime', desc: 'The date of finish of the article'}
102 102 expose :tag_list
103 103 expose :children_count
104 104 end
... ... @@ -109,6 +109,7 @@ module Noosfero
109 109 expose :children, using: ArticleBase do |article, options|
110 110 article.children.limit(Noosfero::API::V1::Articles::MAX_PER_PAGE)
111 111 end
  112 + expose :slug, :documentation => {:type => "String", :desc => "Trimmed and parsed name of a article"}
112 113 end
113 114  
114 115 class Comment < Entity
... ... @@ -138,7 +139,7 @@ module Noosfero
138 139 end
139 140  
140 141 class UserLogin < User
141   - expose :private_token
  142 + expose :private_token, documentation: {type: 'String', desc: 'A valid authentication code for post/delete api actions'}
142 143 end
143 144  
144 145 class Task < Entity
... ...
lib/noosfero/api/helpers.rb
... ... @@ -10,6 +10,7 @@ require &#39;grape&#39;
10 10 include SanitizeParams
11 11 include Noosfero::Plugin::HotSpot
12 12 include ForgotPasswordHelper
  13 + include SearchTermHelper
13 14  
14 15 def set_locale
15 16 I18n.locale = (params[:lang] || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en')
... ... @@ -39,6 +40,24 @@ require &#39;grape&#39;
39 40 @environment
40 41 end
41 42  
  43 + ####################################################################
  44 + #### SEARCH
  45 + ####################################################################
  46 + def find_by_contents(asset, context, scope, query, paginate_options={:page => 1}, options={})
  47 + scope = scope.with_templates(options[:template_id]) unless options[:template_id].blank?
  48 + search = plugins.dispatch_first(:find_by_contents, asset, scope, query, paginate_options, options)
  49 + register_search_term(query, scope.count, search[:results].count, context, asset)
  50 + search
  51 + end
  52 + def paginate_options(page = params[:page])
  53 + page = 1 if multiple_search?(@searches) || params[:display] == 'map'
  54 + { :per_page => limit, :page => page }
  55 + end
  56 + def multiple_search?(searches=nil)
  57 + ['index', 'category_index'].include?(params[:action]) || (searches && searches.size > 1)
  58 + end
  59 + ####################################################################
  60 +
42 61 def logger
43 62 logger = Logger.new(File.join(Rails.root, 'log', "#{ENV['RAILS_ENV'] || 'production'}_api.log"))
44 63 logger.formatter = GrapeLogging::Formatters::Default.new
... ... @@ -99,7 +118,11 @@ require &#39;grape&#39;
99 118  
100 119 def present_articles(asset, method = 'articles')
101 120 articles = find_articles(asset, method)
102   - articles = paginate articles
  121 + present_articles_paginated(articles)
  122 + end
  123 +
  124 + def present_articles_paginated(articles, per_page=nil)
  125 + articles = paginate(articles)
103 126 present articles, :with => Entities::Article, :fields => params[:fields]
104 127 end
105 128  
... ...
lib/noosfero/api/v1/articles.rb
... ... @@ -21,6 +21,19 @@ module Noosfero
21 21 # Example Request:
22 22 # GET host/api/v1/articles?from=2013-04-04-14:41:43&until=2015-04-04-14:41:43&limit=10&private_token=e96fff37c2238fdab074d1dcea8e6317
23 23  
  24 + desc 'Return all articles of all kinds' do
  25 + detail 'Get all articles filtered by fields in query params'
  26 + params Noosfero::API::Entities::Article.documentation
  27 + success Noosfero::API::Entities::Article
  28 + failure [[403, 'Forbidden']]
  29 + named 'ArticlesList'
  30 + headers [
  31 + 'Per-Page' => {
  32 + description: 'Total number of records',
  33 + required: false
  34 + }
  35 + ]
  36 + end
24 37 get do
25 38 present_articles(environment)
26 39 end
... ... @@ -30,8 +43,14 @@ module Noosfero
30 43 present_articles(current_person, 'following_articles')
31 44 end
32 45  
33   - desc "Return the article id"
34   - get ':id' do
  46 + desc "Return one article by id" do
  47 + detail 'Get only one article by id. If not found the "forbidden" http error is showed'
  48 + params Noosfero::API::Entities::Article.documentation
  49 + success Noosfero::API::Entities::Article
  50 + failure [[403, 'Forbidden']]
  51 + named 'ArticleById'
  52 + end
  53 + get ':id', requirements: {id: /[0-9]+/} do
35 54 present_article(environment)
36 55 end
37 56  
... ... @@ -42,6 +61,12 @@ module Noosfero
42 61 present article, :with => Entities::Article, :fields => params[:fields]
43 62 end
44 63  
  64 + desc 'Report a abuse and/or violent content in a article by id' do
  65 + detail 'Submit a abuse (in general, a content violation) report about a specific article'
  66 + params Noosfero::API::Entities::Article.documentation
  67 + failure [[400, 'Bad Request']]
  68 + named 'ArticleReportAbuse'
  69 + end
45 70 post ':id/report_abuse' do
46 71 article = find_article(environment.articles, params[:id])
47 72 profile = article.profile
... ... @@ -70,14 +95,23 @@ module Noosfero
70 95  
71 96 end
72 97  
73   - desc "Returns the total followers for the article"
  98 + desc "Returns the total followers for the article" do
  99 + detail 'Get the followers of a specific article by id'
  100 + failure [[403, 'Forbidden']]
  101 + named 'ArticleFollowers'
  102 + end
74 103 get ':id/followers' do
75 104 article = find_article(environment.articles, params[:id])
76 105 total = article.person_followers.count
77 106 {:total_followers => total}
78 107 end
79 108  
80   - desc "Add a follower for the article"
  109 + desc "Add a follower for the article" do
  110 + detail 'Add the current user identified by private token, like a follower of a article'
  111 + params Noosfero::API::Entities::UserLogin.documentation
  112 + failure [[401, 'Unauthorized']]
  113 + named 'ArticleFollow'
  114 + end
81 115 post ':id/follow' do
82 116 authenticate!
83 117 article = find_article(environment.articles, params[:id])
... ... @@ -92,6 +126,12 @@ module Noosfero
92 126 end
93 127 end
94 128  
  129 + desc 'Perform a vote on a article by id' do
  130 + detail 'Vote on a specific article with values: 1 (if you like) or -1 (if not)'
  131 + params Noosfero::API::Entities::UserLogin.documentation
  132 + failure [[401,'Unauthorized']]
  133 + named 'ArticleVote'
  134 + end
95 135 post ':id/vote' do
96 136 authenticate!
97 137 value = (params[:value] || 1).to_i
... ... @@ -102,13 +142,20 @@ module Noosfero
102 142 {:vote => vote.save}
103 143 end
104 144  
  145 + desc 'Return the children of a article identified by id' do
  146 + detail 'Get all children articles of a specific article'
  147 + params Noosfero::API::Entities::Article.documentation
  148 + failure [[403, 'Forbidden']]
  149 + named 'ArticleChildren'
  150 + end
105 151 get ':id/children' do
106 152 article = find_article(environment.articles, params[:id])
107 153  
108 154 #TODO make tests for this situation
109 155 votes_order = params.delete(:order) if params[:order]=='votes_score'
  156 + articles = select_filtered_collection_of(article, 'children', params)
  157 + articles = articles.display_filter(current_person, article.profile)
110 158  
111   - articles = find_articles(article, 'children')
112 159  
113 160 #TODO make tests for this situation
114 161 if votes_order
... ... @@ -119,6 +166,13 @@ module Noosfero
119 166 present articles, :with => Entities::Article, :fields => params[:fields]
120 167 end
121 168  
  169 + desc 'Return one child of a article identified by id' do
  170 + detail 'Get a child of a specific article'
  171 + params Noosfero::API::Entities::Article.documentation
  172 + success Noosfero::API::Entities::Article
  173 + failure [[403, 'Forbidden']]
  174 + named 'ArticleChild'
  175 + end
122 176 get ':id/children/:child_id' do
123 177 article = find_article(environment.articles, params[:id])
124 178 child = find_article(article.children, params[:child_id])
... ... @@ -126,6 +180,13 @@ module Noosfero
126 180 present child, :with => Entities::Article, :fields => params[:fields]
127 181 end
128 182  
  183 + desc 'Suggest a article to another profile' do
  184 + detail 'Suggest a article to another profile (person, community...)'
  185 + params Noosfero::API::Entities::Article.documentation
  186 + success Noosfero::API::Entities::Task
  187 + failure [[401,'Unauthorized']]
  188 + named 'ArticleSuggest'
  189 + end
129 190 post ':id/children/suggest' do
130 191 authenticate!
131 192 parent_article = environment.articles.find(params[:id])
... ... @@ -144,6 +205,13 @@ module Noosfero
144 205  
145 206 # Example Request:
146 207 # POST api/v1/articles/:id/children?private_token=234298743290432&article[name]=title&article[body]=body
  208 + desc 'Add a child article to a parent identified by id' do
  209 + detail 'Create a new article and associate to a parent'
  210 + params Noosfero::API::Entities::Article.documentation
  211 + success Noosfero::API::Entities::Article
  212 + failure [[401,'Unauthorized']]
  213 + named 'ArticleAddChild'
  214 + end
147 215 post ':id/children' do
148 216 authenticate!
149 217 parent_article = environment.articles.find(params[:id])
... ... @@ -173,11 +241,37 @@ module Noosfero
173 241 resource kind.pluralize.to_sym do
174 242 segment "/:#{kind}_id" do
175 243 resource :articles do
  244 +
  245 + desc "Return all articles associate with a profile of type #{kind}" do
  246 + detail 'Get a list of articles of a profile'
  247 + params Noosfero::API::Entities::Article.documentation
  248 + success Noosfero::API::Entities::Article
  249 + failure [[403, 'Forbidden']]
  250 + named 'ArticlesOfProfile'
  251 + end
176 252 get do
177 253 profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
178   - present_articles(profile)
  254 +
  255 + if params[:path].present?
  256 + article = profile.articles.find_by_path(params[:path])
  257 + if !article || !article.display_to?(current_person)
  258 + article = forbidden!
  259 + end
  260 +
  261 + present article, :with => Entities::Article, :fields => params[:fields]
  262 + else
  263 +
  264 + present_articles(profile)
  265 + end
179 266 end
180 267  
  268 + desc "Return a article associate with a profile of type #{kind}" do
  269 + detail 'Get only one article of a profile'
  270 + params Noosfero::API::Entities::Article.documentation
  271 + success Noosfero::API::Entities::Article
  272 + failure [[403, 'Forbidden']]
  273 + named 'ArticleOfProfile'
  274 + end
181 275 get ':id' do
182 276 profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
183 277 present_article(profile)
... ... @@ -185,6 +279,13 @@ module Noosfero
185 279  
186 280 # Example Request:
187 281 # POST api/v1/{people,communities,enterprises}/:asset_id/articles?private_token=234298743290432&article[name]=title&article[body]=body
  282 + desc "Add a new article associated with a profile of type #{kind}" do
  283 + detail 'Create a new article and associate with a profile'
  284 + params Noosfero::API::Entities::Article.documentation
  285 + success Noosfero::API::Entities::Article
  286 + failure [[403, 'Forbidden']]
  287 + named 'ArticleCreateToProfile'
  288 + end
188 289 post do
189 290 profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
190 291 post_article(profile, params)
... ...
lib/noosfero/api/v1/search.rb 0 → 100644
... ... @@ -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 + paginate per_page: 20, max_per_page: 200
  9 + get do
  10 + # Security checks
  11 + sanitize_params_hash(params)
  12 + # APIHelpers
  13 + asset = :articles
  14 + context = environment
  15 +
  16 + profile = environment.profiles.find(params[:profile_id]) if params[:profile_id]
  17 +
  18 + scope = profile.nil? ? environment.articles.public : profile.articles.public
  19 +
  20 + scope = scope.where(:type => params[:type]) if params[:type] && !(params[:type] == 'Article')
  21 +
  22 + category = params[:category] || ""
  23 +
  24 + query = params[:query] || ""
  25 + order = "more_recent"
  26 +
  27 + options = {:filter => order, :template_id => params[:template_id], :category => category}
  28 +
  29 + search_result = find_by_contents(asset, context, scope, query, paginate_options, options)
  30 +
  31 + articles = search_result[:results]
  32 +
  33 + result = present_articles_paginated(articles)
  34 +
  35 + result
  36 + end
  37 + end
  38 + end
  39 +
  40 + end
  41 + end
  42 + end
  43 +end
... ...
plugins/oauth_client/controllers/public/oauth_client_plugin_public_controller.rb
... ... @@ -36,6 +36,7 @@ class OauthClientPluginPublicController &lt; PublicController
36 36 auth ||= person.oauth_auths.create! profile: person, provider: provider, enabled: true
37 37 if auth.enabled? && provider.enabled?
38 38 self.current_user = person.user
  39 + self.current_user.generate_private_token!
39 40 else
40 41 session[:notice] = _("Can't login with %s") % provider.name
41 42 end
... ...
plugins/oauth_client/db/migrate/20150815173209_add_authorization_data_to_oauth_client_user_provider.rb
... ... @@ -12,8 +12,11 @@ class AddAuthorizationDataToOauthClientUserProvider &lt; ActiveRecord::Migration
12 12  
13 13 add_column :oauth_client_plugin_auths, :profile_id, :integer
14 14 OauthClientPlugin::Auth.find_each batch_size: 50 do |auth|
15   - auth.profile = User.find(auth.user_id).person
16   - auth.save!
  15 + user = User.find_by_id(auth.user_id)
  16 + if user.present?
  17 + auth.profile = user.person
  18 + auth.save!
  19 + end
17 20 end
18 21 remove_column :oauth_client_plugin_auths, :user_id
19 22  
... ...
plugins/oauth_client/test/functional/oauth_client_plugin_public_controller_test.rb
... ... @@ -31,6 +31,17 @@ class OauthClientPluginPublicControllerTest &lt; ActionController::TestCase
31 31 assert_equal user.id, session[:user]
32 32 end
33 33  
  34 + should 'generate private token when login' do
  35 + user = create_user
  36 + auth.info.stubs(:email).returns(user.email)
  37 + auth.info.stubs(:name).returns(user.name)
  38 + session[:provider_id] = provider.id
  39 +
  40 + assert user.reload.private_token.nil?
  41 + get :callback
  42 + assert user.reload.private_token.present?
  43 + end
  44 +
34 45 should 'do not login when the provider is disabled' do
35 46 user = create_user
36 47 auth.info.stubs(:email).returns(user.email)
... ...
test/functional/account_controller_test.rb
... ... @@ -232,6 +232,16 @@ class AccountControllerTest &lt; ActionController::TestCase
232 232 assert_template 'password_recovery_sent'
233 233 end
234 234  
  235 + should 'not respond to forgotten password change if captcha verification fails' do
  236 + create_user('test')
  237 + @controller.stubs(:verify_recaptcha).returns(false)
  238 + post :forgot_password, :value => 'test'
  239 + change = assigns(:change_password)
  240 + assert change.errors.has_key?(:base)
  241 + assert_response :success
  242 + assert_tag :tag => 'div', :attributes => { :id => 'errorExplanation', :class => 'errorExplanation' }
  243 + end
  244 +
235 245 should 'respond to forgotten password change request with email' do
236 246 change = ChangePassword.new
237 247 create_user('test', :email => 'test@localhost.localdomain')
... ...
test/unit/api/articles_test.rb
... ... @@ -91,6 +91,41 @@ class ArticlesTest &lt; ActiveSupport::TestCase
91 91 assert_not_includes json['articles'].map {|a| a['id']}, child.id
92 92 end
93 93  
  94 + should 'follow a article identified by id' do
  95 + article = fast_create(Article, :profile_id => @person.id, :name => "Some thing")
  96 + post "/api/v1/articles/#{article.id}/follow?#{params.to_query}"
  97 + json = JSON.parse(last_response.body)
  98 +
  99 + assert_not_equal 401, last_response.status
  100 + assert_equal true, json['success']
  101 + end
  102 +
  103 + should 'return the followers of a article identified by id' do
  104 + article = fast_create(Article, :profile_id => @person.id, :name => "Some thing")
  105 +
  106 + article_follower = ArticleFollower.new
  107 + article_follower.article = article
  108 + article_follower.person = @person
  109 + article_follower.save!
  110 +
  111 + get "/api/v1/articles/#{article.id}/followers?"
  112 + json = JSON.parse(last_response.body)
  113 +
  114 + assert_equal 200, last_response.status
  115 + assert_equal 1, json['total_followers']
  116 + end
  117 +
  118 + should 'perform a vote in a article identified by id' do
  119 + article = fast_create(Article, :profile_id => @person.id, :name => "Some thing")
  120 + @params[:value] = 1
  121 +
  122 + post "/api/v1/articles/#{article.id}/vote?#{params.to_query}"
  123 + json = JSON.parse(last_response.body)
  124 +
  125 + assert_not_equal 401, last_response.status
  126 + assert_equal true, json['vote']
  127 + end
  128 +
94 129 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)
95 130  
96 131 expose_attributes.each do |attr|
... ... @@ -180,6 +215,29 @@ class ArticlesTest &lt; ActiveSupport::TestCase
180 215 json = JSON.parse(last_response.body)
181 216 assert_not_includes json['articles'].map {|a| a['id']}, article.id
182 217 end
  218 +
  219 + should "return article by #{kind} and path" do
  220 + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id)
  221 + parent_article = Folder.create!(:profile => profile, :name => "Parent Folder")
  222 + article = Article.create!(:profile => profile, :name => "Some thing", :parent => parent_article)
  223 +
  224 + params[:path] = parent_article.slug+'/'+article.slug
  225 + get "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}"
  226 + json = JSON.parse(last_response.body)
  227 + assert_equal article.id, json["article"]["id"]
  228 + end
  229 +
  230 + should "not return article by #{kind} and path if user has no permission to view it" do
  231 + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id)
  232 + parent_article = Folder.create!(:profile => profile, :name => "Parent Folder")
  233 + article = Article.create!(:profile => profile, :name => "Some thing", :parent => parent_article, :published => false)
  234 +
  235 + assert !article.published?
  236 +
  237 + params[:path] = parent_article.slug+'/'+article.slug
  238 + get "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}"
  239 + assert_equal 403, last_response.status
  240 + end
183 241 end
184 242  
185 243 #############################
... ...
test/unit/api/search_test.rb 0 → 100644
... ... @@ -0,0 +1,107 @@
  1 +require File.dirname(__FILE__) + '/test_helper'
  2 +
  3 +class SearchTest < ActiveSupport::TestCase
  4 +
  5 + def create_article_with_optional_category(name, profile, category = nil)
  6 + fast_create(Article, {:name => name, :profile_id => profile.id }, :search => true, :category => category, :title => name)
  7 + end
  8 +
  9 + should 'not list unpublished articles' do
  10 + person = fast_create(Person)
  11 + article = fast_create(Article, :profile_id => person.id, :name => "Some thing", :published => false)
  12 + assert !article.published?
  13 + get "/api/v1/search/article"
  14 + json = JSON.parse(last_response.body)
  15 + assert_empty json['articles']
  16 + end
  17 +
  18 + should 'list articles' do
  19 + person = fast_create(Person)
  20 + art = create_article_with_optional_category('an article to be found', person)
  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 + person = fast_create(Person)
  28 + art = create_article_with_optional_category('an article to be found', person)
  29 + get "/api/v1/search/article?query=test"
  30 + json = JSON.parse(last_response.body)
  31 + assert_empty json['articles']
  32 + end
  33 +
  34 + should 'do not list articles of wrong type' do
  35 + person = fast_create(Person)
  36 + art = create_article_with_optional_category('an article to be found', person)
  37 + get "/api/v1/search/article?type=TinyMceArticle"
  38 + json = JSON.parse(last_response.body)
  39 + assert_empty json['articles']
  40 + end
  41 +
  42 + should 'list articles of one type' do
  43 + person = fast_create(Person)
  44 + art = create_article_with_optional_category('an article to be found', person)
  45 + article = fast_create(TinyMceArticle, :profile_id => person.id, :name => "Some thing", :published => true)
  46 + get "/api/v1/search/article?type=TinyMceArticle"
  47 + json = JSON.parse(last_response.body)
  48 + assert_equal 1, json['articles'].count
  49 + end
  50 +
  51 + should 'list articles of one type and query string' do
  52 + person = fast_create(Person)
  53 + art = create_article_with_optional_category('an article to be found', person)
  54 + art1 = create_article_with_optional_category('article for query', person)
  55 + article = fast_create(TinyMceArticle, :profile_id => person.id, :name => "Some thing", :published => true)
  56 + get "/api/v1/search/article?type=TinyMceArticle&query=thing"
  57 + json = JSON.parse(last_response.body)
  58 + assert_equal 1, json['articles'].count
  59 + end
  60 +
  61 + should 'not return more entries than page limit' do
  62 + person = fast_create(Person)
  63 + ('1'..'5').each do |n|
  64 + art = create_article_with_optional_category("Article #{n}", person)
  65 + end
  66 +
  67 + get "/api/v1/search/article?query=Article&per_page=3"
  68 + json = JSON.parse(last_response.body)
  69 +
  70 + assert_equal 3, json['articles'].count
  71 + end
  72 +
  73 + should 'return entries second page' do
  74 + person = fast_create(Person)
  75 + ('1'..'5').each do |n|
  76 + art = create_article_with_optional_category("Article #{n}", person)
  77 + end
  78 +
  79 + get "/api/v1/search/article?query=Article&per_page=3&page=2"
  80 + json = JSON.parse(last_response.body)
  81 +
  82 + assert_equal 2, json['articles'].count
  83 + end
  84 +
  85 + should 'search articles in profile' do
  86 + person1 = fast_create(Person)
  87 + person2 = fast_create(Person)
  88 +
  89 + art1 = create_article_with_optional_category("Article 1 for profile #{person1.id}", person1)
  90 + art2 = create_article_with_optional_category("Article for profile #{person2.id}", person2)
  91 + art3 = create_article_with_optional_category("Article 2 for profile #{person1.id}", person1)
  92 +
  93 + get "/api/v1/search/article?query=Article&profile_id=#{person1.id}"
  94 + json = JSON.parse(last_response.body)
  95 + # Only for person1
  96 + assert_equal 2, json['articles'].count
  97 + end
  98 +
  99 + should 'search with fields' do
  100 + person = fast_create(Person)
  101 + art = create_article_with_optional_category('an article to be found', person)
  102 + get "/api/v1/search/article?fields=title"
  103 + json = JSON.parse(last_response.body)
  104 + assert_not_empty json['articles']
  105 + assert_equal ['title'], json['articles'].first.keys
  106 + end
  107 +end
0 108 \ No newline at end of file
... ...
test/unit/api/test_helper.rb
... ... @@ -17,6 +17,11 @@ class ActiveSupport::TestCase
17 17 post "/api/v1/login?login=testapi&password=testapi"
18 18 json = JSON.parse(last_response.body)
19 19 @private_token = json["private_token"]
  20 + unless @private_token
  21 + @user.generate_private_token!
  22 + @private_token = @user.private_token
  23 + end
  24 +
20 25 @params = {:private_token => @private_token}
21 26 end
22 27 attr_accessor :private_token, :user, :person, :params, :environment
... ...