Commit b4960cda06578e5516b9cfd560f51c872cbfa69e

Authored by Victor Costa
2 parents b69c8359 2ecd9658

Merge branch 'rails3_api' into rails3_stable

Gemfile
... ... @@ -15,6 +15,7 @@ gem 'thin'
15 15 gem 'hpricot'
16 16 gem 'nokogiri'
17 17 gem 'rake', :require => false
  18 +gem 'grape', '0.2.1'
18 19  
19 20 # FIXME list here all actual dependencies (i.e. the ones in debian/control),
20 21 # with their GEM names (not the Debian package names)
... ...
app/models/article.rb
... ... @@ -102,6 +102,16 @@ class Article < ActiveRecord::Base
102 102 {:include => 'categories_including_virtual', :conditions => { 'categories.id' => category.id }}
103 103 }
104 104  
  105 + #FIXME make this test
  106 + scope :newer_than, lambda { |reference_id|
  107 + {:conditions => ["articles.id > #{reference_id}"]}
  108 + }
  109 +
  110 + #FIXME make this test
  111 + scope :older_than, lambda { |reference_id|
  112 + {:conditions => ["articles.id < #{reference_id}"]}
  113 + }
  114 +
105 115 scope :by_range, lambda { |range| {
106 116 :conditions => [
107 117 'published_at BETWEEN :start_date AND :end_date', { :start_date => range.first, :end_date => range.last }
... ... @@ -658,6 +668,11 @@ class Article &lt; ActiveRecord::Base
658 668 person ? person.id : nil
659 669 end
660 670  
  671 + #FIXME make this test
  672 + def author_custom_image(size = :icon)
  673 + author ? author.profile_custom_image(size) : nil
  674 + end
  675 +
661 676 def version_license(version_number = nil)
662 677 return license if version_number.nil?
663 678 profile.environment.licenses.find_by_id(versions.find_by_version(version_number).license_id)
... ...
app/models/comment.rb
... ... @@ -20,6 +20,17 @@ class Comment &lt; ActiveRecord::Base
20 20  
21 21 scope :without_reply, :conditions => ['reply_of_id IS NULL']
22 22  
  23 + #FIXME make this test
  24 + scope :newer_than, lambda { |reference_id|
  25 + {:conditions => ["comments.id > #{reference_id}"]}
  26 + }
  27 +
  28 + #FIXME make this test
  29 + scope :older_than, lambda { |reference_id|
  30 + {:conditions => ["comments.id < #{reference_id}"]}
  31 + }
  32 +
  33 +
23 34 # unauthenticated authors:
24 35 validates_presence_of :name, :if => (lambda { |record| !record.email.blank? })
25 36 validates_presence_of :email, :if => (lambda { |record| !record.name.blank? })
... ... @@ -63,6 +74,11 @@ class Comment &lt; ActiveRecord::Base
63 74 author ? author.url : nil
64 75 end
65 76  
  77 + #FIXME make this test
  78 + def author_custom_image(size = :icon)
  79 + author ? author.profile_custom_image(size) : nil
  80 + end
  81 +
66 82 def url
67 83 article.view_url.merge(:anchor => anchor)
68 84 end
... ...
app/models/profile.rb
... ... @@ -89,6 +89,16 @@ class Profile &lt; ActiveRecord::Base
89 89 scope :templates, {:conditions => {:is_template => true}}
90 90 scope :no_templates, {:conditions => {:is_template => false}}
91 91  
  92 + #FIXME make this test
  93 + scope :newer_than, lambda { |reference_id|
  94 + {:conditions => ["profiles.id > #{reference_id}"]}
  95 + }
  96 +
  97 + #FIXME make this test
  98 + scope :older_than, lambda { |reference_id|
  99 + {:conditions => ["profiles.id < #{reference_id}"]}
  100 + }
  101 +
92 102 def members
93 103 scopes = plugins.dispatch_scopes(:organization_members, self)
94 104 scopes << Person.members_of(self)
... ... @@ -877,6 +887,13 @@ private :generate_url, :url_options
877 887 image.public_filename(:icon) if image.present?
878 888 end
879 889  
  890 + #FIXME make this test
  891 + def profile_custom_image(size = :icon)
  892 + image_path = profile_custom_icon if size == :icon
  893 + image_path ||= image.public_filename(size) if image.present?
  894 + image_path
  895 + end
  896 +
880 897 def jid(options = {})
881 898 domain = options[:domain] || environment.default_hostname
882 899 "#{identifier}@#{domain}"
... ...
app/models/user.rb
1 1 require 'digest/sha1'
2 2 require 'user_activation_job'
  3 +require 'securerandom'
3 4  
4 5 # User models the system users, and is generated by the acts_as_authenticated
5 6 # Rails generator.
... ... @@ -138,6 +139,18 @@ class User &lt; ActiveRecord::Base
138 139 u && u.authenticated?(password) ? u : nil
139 140 end
140 141  
  142 + #FIXME make this test
  143 + def generate_private_token!
  144 + self.private_token = SecureRandom.hex
  145 + self.private_token_generated_at = DateTime.now
  146 + save(:validate => false)
  147 + end
  148 +
  149 + #FIXME make this test
  150 + def private_token_expired?
  151 + self.generate_private_token! if self.private_token.nil? || (self.private_token_generated_at + 2.weeks < DateTime.now)
  152 + end
  153 +
141 154 # Activates the user in the database.
142 155 def activate
143 156 return false unless self.person
... ...
config.ru
1 1 # This file is used by Rack-based servers to start the application.
2 2  
3 3 require ::File.expand_path('../config/environment', __FILE__)
4   -run Noosfero::Application
  4 +
  5 +rails_app = Rack::Builder.new do
  6 + run Noosfero::Application
  7 +end
  8 +
  9 +run Rack::Cascade.new([
  10 + API::API,
  11 + rails_app
  12 +])
... ...
db/migrate/20140407013817_add_private_token_info_to_users.rb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +class AddPrivateTokenInfoToUsers < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :users, :private_token, :string
  4 + add_column :users, :private_token_generated_at, :datetime
  5 + end
  6 +
  7 + def self.down
  8 + remove_column :users, :private_token
  9 + remove_column :users, :private_token_generated_at
  10 + end
  11 +end
... ...
lib/api/api.rb 0 → 100644
... ... @@ -0,0 +1,22 @@
  1 +require 'grape'
  2 +Dir["#{Rails.root}/lib/api/*.rb"].each {|file| require file}
  3 +
  4 +module API
  5 + class API < Grape::API
  6 + version 'v1'
  7 + prefix "api"
  8 + format :json
  9 + content_type :txt, "text/plain"
  10 +
  11 + helpers APIHelpers
  12 +
  13 + mount V1::Articles
  14 + mount V1::Comments
  15 + mount V1::Users
  16 + mount V1::Communities
  17 + mount V1::People
  18 + mount V1::Enterprises
  19 + mount Session
  20 +
  21 + end
  22 +end
... ...
lib/api/entities.rb 0 → 100644
... ... @@ -0,0 +1,71 @@
  1 +module API
  2 + module Entities
  3 +
  4 + class Image < Grape::Entity
  5 + root 'images', 'image'
  6 +
  7 + expose :icon_url do |image, options|
  8 + image.public_filename(:icon)
  9 + end
  10 +
  11 + expose :minor_url do |image, options|
  12 + image.public_filename(:minor)
  13 + end
  14 +
  15 + expose :portrait_url do |image, options|
  16 + image.public_filename(:portrait)
  17 + end
  18 +
  19 + expose :thumb_url do |image, options|
  20 + image.public_filename(:thumb)
  21 + end
  22 + end
  23 +
  24 + class Profile < Grape::Entity
  25 + expose :identifier, :name, :created_at, :id
  26 + expose :image, :using => Image
  27 + end
  28 +
  29 + class Person < Profile;end;
  30 + class Enterprise < Profile;end;
  31 + class Community < Profile
  32 + root 'communities', 'community'
  33 + expose :description
  34 + end
  35 +
  36 + class Category < Grape::Entity
  37 + root 'categories', 'category'
  38 + expose :name, :id, :slug
  39 + expose :image, :using => Image
  40 + end
  41 +
  42 +
  43 + class Article < Grape::Entity
  44 + root 'articles', 'article'
  45 + expose :id, :body, :created_at
  46 + expose :title, :documentation => {:type => "String", :desc => "Title of the article"}
  47 + expose :author, :using => Profile
  48 + expose :profile, :using => Profile
  49 + expose :categories, :using => Category
  50 + end
  51 +
  52 + class Comment < Grape::Entity
  53 + root 'comments', 'comment'
  54 + expose :body, :title, :created_at, :id
  55 +
  56 + expose :author, :using => Profile
  57 + end
  58 +
  59 +
  60 + class User < Grape::Entity
  61 + root 'users', 'user'
  62 + expose :login
  63 + expose :person, :using => Profile
  64 + end
  65 +
  66 + class UserLogin < User
  67 + expose :private_token
  68 + end
  69 +
  70 + end
  71 +end
... ...
lib/api/helpers.rb 0 → 100644
... ... @@ -0,0 +1,190 @@
  1 +module API
  2 + module APIHelpers
  3 + PRIVATE_TOKEN_PARAM = :private_token
  4 +
  5 + def logger
  6 + API.logger
  7 + end
  8 +
  9 + def current_user
  10 + private_token = params[PRIVATE_TOKEN_PARAM].to_s
  11 + @current_user ||= User.find_by_private_token(private_token)
  12 + @current_user = nil if !@current_user.nil? && @current_user.private_token_expired?
  13 + @current_user
  14 + end
  15 +
  16 + def current_person
  17 + current_user.person unless current_user.nil?
  18 + end
  19 +
  20 + def logout
  21 + @current_user = nil
  22 + end
  23 +
  24 + def environment
  25 + @environment
  26 + end
  27 +
  28 + def limit
  29 + limit = params[:limit].to_i
  30 + limit = default_limit if limit <= 0
  31 + limit
  32 + end
  33 +
  34 + def period(from_date, until_date)
  35 + begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date
  36 + end_period = until_date.nil? ? DateTime.now : until_date
  37 +
  38 + begin_period...end_period
  39 + end
  40 +
  41 + def parse_content_type(content_type)
  42 + return nil if content_type.blank?
  43 + content_type.split(',').map do |content_type|
  44 + content_type.camelcase
  45 + end
  46 + end
  47 +
  48 + def make_conditions_with_parameter(params = {})
  49 + conditions = {}
  50 + from_date = DateTime.parse(params[:from]) if params[:from]
  51 + until_date = DateTime.parse(params[:until]) if params[:until]
  52 +
  53 + conditions[:type] = parse_content_type(params[:content_type]) unless params[:content_type].nil?
  54 +
  55 + conditions[:created_at] = period(from_date, until_date) if from_date || until_date
  56 +
  57 + conditions
  58 + end
  59 +
  60 +
  61 + def select_filtered_collection_of(object, method, params)
  62 + conditions = make_conditions_with_parameter(params)
  63 +
  64 + if params[:reference_id]
  65 + objects = object.send(method).send("#{params.key?(:oldest) ? 'older_than' : 'newer_than'}", params[:reference_id]).find(:all, :conditions => conditions, :limit => limit, :order => "created_at DESC")
  66 + else
  67 + objects = object.send(method).find(:all, :conditions => conditions, :limit => limit, :order => "created_at DESC")
  68 + end
  69 + objects
  70 + end
  71 +
  72 +#FIXME see if its needed
  73 +# def paginate(relation)
  74 +# per_page = params[:per_page].to_i
  75 +# paginated = relation.page(params[:page]).per(per_page)
  76 +# add_pagination_headers(paginated, per_page)
  77 +#
  78 +# paginated
  79 +# end
  80 +
  81 + def authenticate!
  82 + unauthorized! unless current_user
  83 + end
  84 +
  85 +#FIXME see if its needed
  86 +# def authenticated_as_admin!
  87 +# forbidden! unless current_user.is_admin?
  88 +# end
  89 +#
  90 +#FIXME see if its needed
  91 +# def authorize! action, subject
  92 +# unless abilities.allowed?(current_user, action, subject)
  93 +# forbidden!
  94 +# end
  95 +# end
  96 +#
  97 +#FIXME see if its needed
  98 +# def can?(object, action, subject)
  99 +# abilities.allowed?(object, action, subject)
  100 +# end
  101 +
  102 + # Checks the occurrences of required attributes, each attribute must be present in the params hash
  103 + # or a Bad Request error is invoked.
  104 + #
  105 + # Parameters:
  106 + # keys (required) - A hash consisting of keys that must be present
  107 + def required_attributes!(keys)
  108 + keys.each do |key|
  109 + bad_request!(key) unless params[key].present?
  110 + end
  111 + end
  112 +
  113 + # Checks the occurrences of uniqueness of attributes, each attribute must be present in the params hash
  114 + # or a Bad Request error is invoked.
  115 + #
  116 + # Parameters:
  117 + # keys (unique) - A hash consisting of keys that must be unique
  118 + def unique_attributes!(obj, keys)
  119 + keys.each do |key|
  120 + cant_be_saved_request!(key) if obj.send("find_by_#{key.to_s}", params[key])
  121 + end
  122 + end
  123 +
  124 + def attributes_for_keys(keys)
  125 + attrs = {}
  126 + keys.each do |key|
  127 + attrs[key] = params[key] if params[key].present? or (params.has_key?(key) and params[key] == false)
  128 + end
  129 + attrs
  130 + end
  131 +
  132 + # error helpers
  133 + def forbidden!
  134 + render_api_error!('403 Forbidden', 403)
  135 + end
  136 +
  137 + def cant_be_saved_request!(attribute)
  138 + message = _("(Invalid request) #{attribute} can't be saved")
  139 + render_api_error!(message, 400)
  140 + end
  141 +
  142 + def bad_request!(attribute)
  143 + message = _("(Bad request) #{attribute} not given")
  144 + render_api_error!(message, 400)
  145 + end
  146 +
  147 + def something_wrong!
  148 + message = _("Something wrong happened")
  149 + render_api_error!(message, 400)
  150 + end
  151 +
  152 + def unauthorized!
  153 + render_api_error!(_('Unauthorized'), 401)
  154 + end
  155 +
  156 + def not_allowed!
  157 + render_api_error!(_('Method Not Allowed'), 405)
  158 + end
  159 +
  160 + def render_api_error!(message, status)
  161 + error!({'message' => message, :code => status}, status)
  162 + end
  163 +
  164 + def render_api_errors!(messages)
  165 + render_api_error!(messages.join(','), 400)
  166 + end
  167 + protected
  168 +
  169 + def detect_stuff_by_domain
  170 + @domain = Domain.find_by_name(request.host)
  171 + if @domain.nil?
  172 + @environment = Environment.default
  173 + if @environment.nil? && Rails.env.development?
  174 + # This should only happen in development ...
  175 + @environment = Environment.create!(:name => "Noosfero", :is_default => true)
  176 + end
  177 + else
  178 + @environment = @domain.environment
  179 + end
  180 + end
  181 +
  182 + private
  183 +
  184 + def default_limit
  185 + 20
  186 + end
  187 +
  188 +
  189 + end
  190 +end
... ...
lib/api/session.rb 0 → 100644
... ... @@ -0,0 +1,44 @@
  1 +module API
  2 +
  3 + class Session < Grape::API
  4 +
  5 + # Login to get token
  6 + #
  7 + # Parameters:
  8 + # login (*required) - user login or email
  9 + # password (required) - user password
  10 + #
  11 + # Example Request:
  12 + # POST /login?login=some&password=pass
  13 + post "/login" do
  14 + user ||= User.authenticate(params[:login], params[:password], environment)
  15 +
  16 + return unauthorized! unless user
  17 + user.generate_private_token!
  18 + present user, :with => Entities::UserLogin
  19 + end
  20 +
  21 + # Create user.
  22 + #
  23 + # Parameters:
  24 + # email (required) - Email
  25 + # password (required) - Password
  26 + # login - login
  27 + # Example Request:
  28 + # POST /register?email=some@mail.com&password=pas&login=some
  29 + post "/register" do
  30 + required_attributes! [:email, :login, :password]
  31 + unique_attributes! User, [:email, :login]
  32 + attrs = attributes_for_keys [:email, :login, :password]
  33 + attrs[:password_confirmation] = attrs[:password]
  34 + user = User.new(attrs)
  35 + if user.save
  36 + user.activate
  37 + present user, :with => Entities::User
  38 + else
  39 + something_wrong!
  40 + end
  41 + end
  42 +
  43 + end
  44 +end
... ...
lib/api/v1/articles.rb 0 → 100644
... ... @@ -0,0 +1,83 @@
  1 +module API
  2 + module V1
  3 + class Articles < Grape::API
  4 + before { detect_stuff_by_domain }
  5 + before { authenticate! }
  6 +
  7 + resource :articles do
  8 +
  9 + # Collect comments from articles
  10 + #
  11 + # Parameters:
  12 + # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created
  13 + # oldest - Collect the oldest comments from reference_id comment. If nothing is passed the newest comments are collected
  14 + # limit - amount of comments returned. The default value is 20
  15 + #
  16 + # Example Request:
  17 + # GET /articles?from=2013-04-04-14:41:43&until=2014-04-04-14:41:43&limit=10&content_type=Hub
  18 +# desc 'Articles.', {
  19 +# :params => API::Entities::Article.documentation
  20 +# }
  21 + get do
  22 +
  23 + articles = select_filtered_collection_of(environment, 'articles', params)
  24 + present articles, :with => Entities::Article
  25 + end
  26 +
  27 + desc "Return the article id"
  28 + get ':id' do
  29 + present environment.articles.find(params[:id]), :with => Entities::Article
  30 + end
  31 +
  32 + get ':id/children' do
  33 +
  34 + conditions = make_conditions_with_parameter(params)
  35 + if params[:reference_id]
  36 + articles = environment.articles.find(params[:id]).children.send("#{params.key?(:oldest) ? 'older_than' : 'newer_than'}", params[:reference_id]).find(:all, :conditions => conditions, :limit => limit, :order => "created_at DESC")
  37 + else
  38 + articles = environment.articles.find(params[:id]).children.find(:all, :conditions => conditions, :limit => limit, :order => "created_at DESC")
  39 + end
  40 + present articles, :with => Entities::Article
  41 + end
  42 +
  43 + get ':id/children/:child_id' do
  44 + present environment.articles.find(params[:id]).children.find(params[:child_id]), :with => Entities::Article
  45 + end
  46 +
  47 +
  48 + end
  49 +
  50 + resource :communities do
  51 + segment '/:community_id' do
  52 + resource :articles do
  53 + get do
  54 + community = environment.communities.find(params[:community_id])
  55 + articles = select_filtered_collection_of(community, 'articles', params)
  56 + present articles, :with => Entities::Article
  57 + end
  58 +
  59 + get '/:id' do
  60 + community = environment.communities.find(params[:community_id])
  61 + present community.articles.find(params[:id]), :with => Entities::Article
  62 + end
  63 +
  64 + # Example Request:
  65 + # POST api/v1/communites/:community_id/articles?private_toke=234298743290432&article[name]=title&article[body]=body
  66 + post do
  67 + community = environment.communities.find(params[:community_id])
  68 + article = community.articles.build(params[:article].merge(:last_changed_by => current_person))
  69 + article.type= params[:type].nil? ? 'TinyMceArticle' : params[:type]
  70 + if !article.save
  71 + render_api_errors!(article.errors.full_messages)
  72 + end
  73 + present article, :with => Entities::Article
  74 + end
  75 +
  76 + end
  77 + end
  78 +
  79 + end
  80 +
  81 + end
  82 + end
  83 +end
... ...
lib/api/v1/comments.rb 0 → 100644
... ... @@ -0,0 +1,44 @@
  1 +module API
  2 + module V1
  3 + class Comments < Grape::API
  4 +
  5 + before { detect_stuff_by_domain }
  6 + before { authenticate! }
  7 +
  8 + resource :articles do
  9 + # Collect comments from articles
  10 + #
  11 + # Parameters:
  12 + # reference_id - comment id used as reference to collect comment
  13 + # oldest - Collect the oldest comments from reference_id comment. If nothing is passed the newest comments are collected
  14 + # limit - amount of comments returned. The default value is 20
  15 + #
  16 + # Example Request:
  17 + # GET /articles/12/comments?oldest&limit=10&reference_id=23
  18 + get ":id/comments" do
  19 +
  20 + conditions = make_conditions_with_parameter(params)
  21 +
  22 + if params[:reference_id]
  23 + comments = environment.articles.find(params[:id]).comments.send("#{params.key?(:oldest) ? 'older_than' : 'newer_than'}", params[:reference_id]).find(:all, :conditions => conditions, :limit => limit, :order => "created_at DESC")
  24 + else
  25 + comments = environment.articles.find(params[:id]).comments.find(:all, :conditions => conditions, :limit => limit, :order => "created_at DESC")
  26 + end
  27 + present comments, :with => Entities::Comment
  28 +
  29 + end
  30 +
  31 + get ":id/comments/:comment_id" do
  32 + present environment.articles.find(params[:id]).comments.find(params[:comment_id]), :with => Entities::Comment
  33 + end
  34 +
  35 + # Example Request:
  36 + # POST api/v1/articles/12/comments?private_toke=234298743290432&body=new comment
  37 + post ":id/comments" do
  38 + present environment.articles.find(params[:id]).comments.create(:author => current_person, :body => params[:body]), :with => Entities::Comment
  39 + end
  40 + end
  41 +
  42 + end
  43 + end
  44 +end
... ...
lib/api/v1/communities.rb 0 → 100644
... ... @@ -0,0 +1,39 @@
  1 +module API
  2 + module V1
  3 + class Communities < Grape::API
  4 + before { detect_stuff_by_domain }
  5 + before { authenticate! }
  6 +
  7 + resource :communities do
  8 +
  9 + # Collect comments from articles
  10 + #
  11 + # Parameters:
  12 + # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created
  13 + # oldest - Collect the oldest comments from reference_id comment. If nothing is passed the newest comments are collected
  14 + # limit - amount of comments returned. The default value is 20
  15 + #
  16 + # Example Request:
  17 + # GET /communities?from=2013-04-04-14:41:43&until=2014-04-04-14:41:43&limit=10
  18 + # GET /communities?reference_id=10&limit=10&oldest
  19 + get do
  20 + communities = select_filtered_collection_of(current_person, 'communities', params)
  21 + present communities, :with => Entities::Community
  22 + end
  23 +
  24 + #FIXME See only public communities
  25 + get '/all' do
  26 + communities = select_filtered_collection_of(environment, 'communities', params)
  27 + present communities, :with => Entities::Community
  28 + end
  29 +
  30 + get ':id' do
  31 + community = environment.communities.find(params[:id])
  32 + present community, :with => Entities::Community
  33 + end
  34 +
  35 + end
  36 +
  37 + end
  38 + end
  39 +end
... ...
lib/api/v1/enterprises.rb 0 → 100644
... ... @@ -0,0 +1,33 @@
  1 +module API
  2 + module V1
  3 + class Enterprises < Grape::API
  4 + before { detect_stuff_by_domain }
  5 + before { authenticate! }
  6 +
  7 + resource :enterprises do
  8 +
  9 + # Collect comments from articles
  10 + #
  11 + # Parameters:
  12 + # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created
  13 + # oldest - Collect the oldest comments from reference_id comment. If nothing is passed the newest comments are collected
  14 + # limit - amount of comments returned. The default value is 20
  15 + #
  16 + # Example Request:
  17 + # GET /enterprises?from=2013-04-04-14:41:43&until=2014-04-04-14:41:43&limit=10
  18 + # GET /enterprises?reference_id=10&limit=10&oldest
  19 + get do
  20 + enterprises = select_filtered_collection_of(environment, 'enterprises', params)
  21 + present enterprises, :with => Entities::Enterprise
  22 + end
  23 +
  24 + desc "Return the article id"
  25 + get ':id' do
  26 + present environment.enterprises.find(params[:id]), :with => Entities::Enterprise
  27 + end
  28 +
  29 + end
  30 +
  31 + end
  32 + end
  33 +end
... ...
lib/api/v1/people.rb 0 → 100644
... ... @@ -0,0 +1,33 @@
  1 +module API
  2 + module V1
  3 + class People < Grape::API
  4 + before { detect_stuff_by_domain }
  5 + before { authenticate! }
  6 +
  7 + resource :people do
  8 +
  9 + # Collect comments from articles
  10 + #
  11 + # Parameters:
  12 + # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created
  13 + # oldest - Collect the oldest comments from reference_id comment. If nothing is passed the newest comments are collected
  14 + # limit - amount of comments returned. The default value is 20
  15 + #
  16 + # Example Request:
  17 + # GET /people?from=2013-04-04-14:41:43&until=2014-04-04-14:41:43&limit=10
  18 + # GET /people?reference_id=10&limit=10&oldest
  19 + get do
  20 + people = select_filtered_collection_of(environment, 'people', params)
  21 + present people, :with => Entities::Person
  22 + end
  23 +
  24 + desc "Return the person information"
  25 + get '/:id' do
  26 + present environment.people.find(params[:id]), :with => Entities::Person
  27 + end
  28 +
  29 + end
  30 +
  31 + end
  32 + end
  33 +end
... ...
lib/api/v1/users.rb 0 → 100644
... ... @@ -0,0 +1,22 @@
  1 +module API
  2 + module V1
  3 + class Users < Grape::API
  4 +
  5 + before { detect_stuff_by_domain }
  6 + before { authenticate! }
  7 +
  8 + resource :users do
  9 +
  10 + get do
  11 + present environment.users, :with => Entities::User
  12 + end
  13 +
  14 + get ":id" do
  15 + present environment.users.find(params[:id]), :with => Entities::User
  16 + end
  17 +
  18 + end
  19 +
  20 + end
  21 + end
  22 +end
... ...
test/unit/api_test.rb 0 → 100644
... ... @@ -0,0 +1,85 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class APITest < ActiveSupport::TestCase
  4 +
  5 + include Rack::Test::Methods
  6 +
  7 + def app
  8 + API::API
  9 + end
  10 +
  11 + def setup
  12 + @user = User.create!(:login => 'testapi', :password => 'testapi', :password_confirmation => 'testapi', :email => 'test@test.org', :environment => Environment.default)
  13 + @user.activate
  14 +
  15 + post "/api/v1/login?login=testapi&password=testapi"
  16 + json = JSON.parse(last_response.body)
  17 + @private_token = json["private_token"]
  18 + @params = {:private_token => @private_token}
  19 + end
  20 + attr_accessor :private_token, :user, :params
  21 +
  22 + should 'generate private token when login' do
  23 + params = {:login => "testapi", :password => "testapi"}
  24 + post "/api/v1/login?#{params.to_query}"
  25 + json = JSON.parse(last_response.body)
  26 + assert !json["private_token"].blank?
  27 + end
  28 +
  29 + should 'return 401 when login fails' do
  30 + user.destroy
  31 + params = {:login => "testapi", :password => "testapi"}
  32 + post "/api/v1/login?#{params.to_query}"
  33 + assert_equal 401, last_response.status
  34 + end
  35 +
  36 + should 'register a user' do
  37 + params = {:login => "newuserapi", :password => "newuserapi", :email => "newuserapi@email.com" }
  38 + post "/api/v1/register?#{params.to_query}"
  39 + assert_equal 201, last_response.status
  40 + end
  41 +
  42 + should 'do not register a user without email' do
  43 + params = {:login => "newuserapi", :password => "newuserapi", :email => nil }
  44 + post "/api/v1/register?#{params.to_query}"
  45 + assert_equal 400, last_response.status
  46 + end
  47 +
  48 + should 'do not register a duplicated user' do
  49 + params = {:login => "newuserapi", :password => "newuserapi", :email => "newuserapi@email.com" }
  50 + post "/api/v1/register?#{params.to_query}"
  51 + post "/api/v1/register?#{params.to_query}"
  52 + assert_equal 400, last_response.status
  53 + end
  54 +
  55 + should 'list articles' do
  56 + article = fast_create(Article, :profile_id => user.person.id, :name => "Some thing")
  57 + get "/api/v1/articles/?#{params.to_query}"
  58 + json = JSON.parse(last_response.body)
  59 + assert_includes json["articles"].map { |a| a["id"] }, article.id
  60 + end
  61 +
  62 + should 'return article by id' do
  63 + article = fast_create(Article, :profile_id => user.person.id, :name => "Some thing")
  64 + get "/api/v1/articles/#{article.id}?#{params.to_query}"
  65 + json = JSON.parse(last_response.body)
  66 + assert_equal article.id, json["article"]["id"]
  67 + end
  68 +
  69 + should 'return comments of an article' do
  70 + article = fast_create(Article, :profile_id => user.person.id, :name => "Some thing")
  71 + article.comments.create!(:body => "some comment", :author => user.person)
  72 + article.comments.create!(:body => "another comment", :author => user.person)
  73 +
  74 + get "/api/v1/articles/#{article.id}/comments?#{params.to_query}"
  75 + json = JSON.parse(last_response.body)
  76 + assert_equal 2, json["comments"].length
  77 + end
  78 +
  79 + should 'list users' do
  80 + get "/api/v1/users/?#{params.to_query}"
  81 + json = JSON.parse(last_response.body)
  82 + assert_includes json["users"].map { |a| a["login"] }, user.login
  83 + end
  84 +
  85 +end
... ...