refinerycms-api 1.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.rbenv-gemsets +1 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +12 -0
  6. data/Gemfile +57 -0
  7. data/LICENSE +27 -0
  8. data/Rakefile +21 -0
  9. data/app/controllers/refinery/api/base_controller.rb +174 -0
  10. data/app/controllers/refinery/api/v1/blog/posts_controller.rb +78 -0
  11. data/app/controllers/refinery/api/v1/images_controller.rb +76 -0
  12. data/app/controllers/refinery/api/v1/inquiries/inquiries_controller.rb +69 -0
  13. data/app/controllers/refinery/api/v1/pages_controller.rb +77 -0
  14. data/app/controllers/refinery/api/v1/resources_controller.rb +66 -0
  15. data/app/decorators/models/refinery/authentication/devise/user_decorator.rb +5 -0
  16. data/app/helpers/refinery/api/api_helpers.rb +54 -0
  17. data/app/models/concerns/refinery/user_api_authentication.rb +19 -0
  18. data/app/models/refinery/ability.rb +59 -0
  19. data/app/views/refinery/api/errors/gateway_error.v1.rabl +2 -0
  20. data/app/views/refinery/api/errors/invalid_api_key.v1.rabl +2 -0
  21. data/app/views/refinery/api/errors/invalid_resource.v1.rabl +3 -0
  22. data/app/views/refinery/api/errors/must_specify_api_key.v1.rabl +2 -0
  23. data/app/views/refinery/api/errors/not_found.v1.rabl +2 -0
  24. data/app/views/refinery/api/errors/unauthorized.v1.rabl +2 -0
  25. data/app/views/refinery/api/v1/blog/posts/index.v1.rabl +5 -0
  26. data/app/views/refinery/api/v1/blog/posts/new.v1.rabl +3 -0
  27. data/app/views/refinery/api/v1/blog/posts/show.v1.rabl +5 -0
  28. data/app/views/refinery/api/v1/images/index.v1.rabl +5 -0
  29. data/app/views/refinery/api/v1/images/new.v1.rabl +3 -0
  30. data/app/views/refinery/api/v1/images/show.v1.rabl +6 -0
  31. data/app/views/refinery/api/v1/inquiries/inquiries/index.v1.rabl +5 -0
  32. data/app/views/refinery/api/v1/inquiries/inquiries/new.v1.rabl +3 -0
  33. data/app/views/refinery/api/v1/inquiries/inquiries/show.v1.rabl +5 -0
  34. data/app/views/refinery/api/v1/pages/index.v1.rabl +8 -0
  35. data/app/views/refinery/api/v1/pages/new.v1.rabl +3 -0
  36. data/app/views/refinery/api/v1/pages/pages.v1.rabl +5 -0
  37. data/app/views/refinery/api/v1/pages/show.v1.rabl +9 -0
  38. data/app/views/refinery/api/v1/resources/index.v1.rabl +5 -0
  39. data/app/views/refinery/api/v1/resources/new.v1.rabl +3 -0
  40. data/app/views/refinery/api/v1/resources/show.v1.rabl +6 -0
  41. data/bin/rails +5 -0
  42. data/bin/rake +21 -0
  43. data/bin/rspec +22 -0
  44. data/bin/spring +18 -0
  45. data/config/initializers/metal_load_paths.rb +1 -0
  46. data/config/locales/en.yml +27 -0
  47. data/config/routes.rb +24 -0
  48. data/db/migrate/20160501141738_add_api_key_to_refinery_authentication_devise_users.rb +8 -0
  49. data/lib/generators/refinery/api/api_generator.rb +16 -0
  50. data/lib/generators/refinery/api/templates/config/initializers/refinery/api.rb.erb +7 -0
  51. data/lib/refinery/api.rb +29 -0
  52. data/lib/refinery/api/configuration.rb +32 -0
  53. data/lib/refinery/api/controller_helpers/auth.rb +76 -0
  54. data/lib/refinery/api/controller_helpers/strong_parameters.rb +37 -0
  55. data/lib/refinery/api/controller_setup.rb +20 -0
  56. data/lib/refinery/api/engine.rb +52 -0
  57. data/lib/refinery/api/responders.rb +11 -0
  58. data/lib/refinery/api/responders/rabl_template.rb +30 -0
  59. data/lib/refinery/api/testing_support/caching.rb +10 -0
  60. data/lib/refinery/api/testing_support/helpers.rb +44 -0
  61. data/lib/refinery/api/testing_support/setup.rb +16 -0
  62. data/lib/refinery/permitted_attributes.rb +40 -0
  63. data/lib/refinery/responder.rb +45 -0
  64. data/lib/refinerycms-api.rb +3 -0
  65. data/readme.md +69 -0
  66. data/refinerycms_api.gemspec +22 -0
  67. data/script/rails +9 -0
  68. data/spec/controllers/refinery/api/base_controller_spec.rb +73 -0
  69. data/spec/controllers/refinery/api/v1/blog/posts_controller_spec.rb +140 -0
  70. data/spec/controllers/refinery/api/v1/images_controller_spec.rb +93 -0
  71. data/spec/controllers/refinery/api/v1/inquiries/inquiries_controller_spec.rb +126 -0
  72. data/spec/controllers/refinery/api/v1/pages_controller_spec.rb +150 -0
  73. data/spec/controllers/refinery/api/v1/resources_controller_spec.rb +94 -0
  74. data/spec/fixtures/refinery_is_awesome.txt +1 -0
  75. data/spec/fixtures/thinking-cat.jpg +0 -0
  76. data/spec/models/refinery/user_spec.rb +23 -0
  77. data/spec/requests/rabl_cache_spec.rb +17 -0
  78. data/spec/requests/ransackable_attributes_spec.rb +80 -0
  79. data/spec/requests/version_spec.rb +23 -0
  80. data/spec/shared_examples/protect_product_actions.rb +17 -0
  81. data/spec/spec_helper.rb +77 -0
  82. data/spec/support/controller_hacks.rb +33 -0
  83. data/spec/support/database_cleaner.rb +14 -0
  84. data/spec/support/have_attributes_matcher.rb +9 -0
  85. data/tasks/refinery_api.rake +14 -0
  86. data/tasks/rspec.rake +4 -0
  87. metadata +240 -0
@@ -0,0 +1,69 @@
1
+ if defined?(Refinery::Inquiries)
2
+ module Refinery
3
+ module Api
4
+ module V1
5
+ module Inquiries
6
+ class InquiriesController < Refinery::Api::BaseController
7
+
8
+ def index
9
+ if params[:ids]
10
+ @inquiries = Refinery::Inquiries::Inquiry.
11
+ accessible_by(current_ability, :read).
12
+ where(id: params[:ids].split(','))
13
+ else
14
+ @inquiries = Refinery::Inquiries::Inquiry.
15
+ accessible_by(current_ability, :read).
16
+ # ransack(params[:q]).result
17
+ order("created_at DESC")
18
+ end
19
+
20
+ respond_with(@inquiries)
21
+ end
22
+
23
+ def show
24
+ @inquiry = inquiry
25
+ respond_with(@inquiry)
26
+ end
27
+
28
+ def new
29
+ end
30
+
31
+ def create
32
+ authorize! :create, ::Refinery::Inquiries::Inquiry
33
+ @inquiry = Refinery::Inquiries::Inquiry.new(inquiry_params)
34
+
35
+ if @inquiry.save
36
+ respond_with(@inquiry, status: 201, default_template: :show)
37
+ else
38
+ invalid_resource!(@inquiry)
39
+ end
40
+ end
41
+
42
+ def destroy
43
+ authorize! :destroy, inquiry
44
+ inquiry.destroy
45
+ respond_with(inquiry, status: 204)
46
+ end
47
+
48
+ private
49
+
50
+ def inquiry
51
+ @inquiry ||= Refinery::Inquiries::Inquiry.
52
+ accessible_by(current_ability, :read).
53
+ find(params[:id])
54
+ end
55
+
56
+ def inquiry_params
57
+ if params[:inquiry] && !params[:inquiry].empty?
58
+ params.require(:inquiry).permit(permitted_inquiries_inquiry_attributes)
59
+ else
60
+ {}
61
+ end
62
+ end
63
+
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,77 @@
1
+ module Refinery
2
+ module Api
3
+ module V1
4
+ class PagesController < Refinery::Api::BaseController
5
+
6
+ def index
7
+ if params[:ids]
8
+ @pages = Refinery::Page.
9
+ includes(:translations, :children).
10
+ accessible_by(current_ability, :read).
11
+ where(id: params[:ids].split(','))
12
+ else
13
+ @pages = Refinery::Page.
14
+ includes(:translations, :children).
15
+ accessible_by(current_ability, :read).
16
+ # ransack(params[:q]).result
17
+ order(:lft)
18
+ end
19
+
20
+ respond_with(@pages)
21
+ end
22
+
23
+ def show
24
+ @page = page
25
+ respond_with(@page)
26
+ end
27
+
28
+ def new
29
+ end
30
+
31
+ def create
32
+ authorize! :create, Page
33
+ @page = Refinery::Page.new(page_params)
34
+
35
+ if @page.save
36
+ respond_with(@page, status: 201, default_template: :show)
37
+ else
38
+ invalid_resource!(@page)
39
+ end
40
+ end
41
+
42
+ def update
43
+ authorize! :update, page
44
+ if page.update_attributes(page_params)
45
+ respond_with(page, status: 200, default_template: :show)
46
+ else
47
+ invalid_resource!(page)
48
+ end
49
+ end
50
+
51
+ def destroy
52
+ authorize! :destroy, page
53
+ page.destroy
54
+ respond_with(page, status: 204)
55
+ end
56
+
57
+ private
58
+
59
+ def page
60
+ @page ||= Refinery::Page.
61
+ includes(:translations, :parts).
62
+ accessible_by(current_ability, :read).
63
+ find(params[:id])
64
+ end
65
+
66
+ def page_params
67
+ if params[:page] && !params[:page].empty?
68
+ params.require(:page).permit(permitted_page_attributes)
69
+ else
70
+ {}
71
+ end
72
+ end
73
+
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,66 @@
1
+ module Refinery
2
+ module Api
3
+ module V1
4
+ class ResourcesController < Refinery::Api::BaseController
5
+ def index
6
+ if params[:ids]
7
+ @resources = Refinery::Resource.
8
+ includes(:translations).
9
+ accessible_by(current_ability, :read).
10
+ where(id: params[:ids].split(','))
11
+ else
12
+ @resources = Refinery::Resource.
13
+ includes(:translations).
14
+ accessible_by(current_ability, :read).
15
+ # load.ransack(params[:q]).result
16
+ all
17
+ end
18
+ respond_with(@resources)
19
+ end
20
+
21
+ def show
22
+ @resource = Refinery::Resource.
23
+ includes(:translations).
24
+ accessible_by(current_ability, :read).
25
+ find(params[:id])
26
+ respond_with(@resource)
27
+ end
28
+
29
+ def new
30
+ end
31
+
32
+ def create
33
+ authorize! :create, Resource
34
+ @resources = Refinery::Resource.create_resources(resource_params)
35
+
36
+ if @resources.all?(&:valid?)
37
+ respond_with(@resources, status: 201, default_template: :show)
38
+ else
39
+ invalid_resource!(@resources)
40
+ end
41
+ end
42
+
43
+ def update
44
+ @resource = Refinery::Resource.accessible_by(current_ability, :update).find(params[:id])
45
+ if @resource.update_attributes(resource_params)
46
+ respond_with(@resource, default_template: :show)
47
+ else
48
+ invalid_resource!(@resource)
49
+ end
50
+ end
51
+
52
+ def destroy
53
+ @resource = Refinery::Resource.accessible_by(current_ability, :destroy).find(params[:id])
54
+ @resource.destroy
55
+ respond_with(@resource, status: 204)
56
+ end
57
+
58
+ private
59
+
60
+ def resource_params
61
+ params.require(:resource).permit(permitted_resource_attributes)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,5 @@
1
+ if defined?(Refinery::Api.user_class)
2
+ Refinery::Api.user_class.class_eval do
3
+ include Refinery::UserApiAuthentication
4
+ end
5
+ end
@@ -0,0 +1,54 @@
1
+ module Refinery
2
+ module Api
3
+ module ApiHelpers
4
+ ATTRIBUTES = [
5
+ :image_attributes,
6
+ :page_attributes,
7
+ :page_part_attributes,
8
+ :resource_attributes,
9
+ :blog_post_attributes,
10
+ :inquiries_inquiry_attributes
11
+ ]
12
+
13
+ mattr_reader *ATTRIBUTES
14
+
15
+ def required_fields_for(model)
16
+ required_fields = model._validators.select do |field, validations|
17
+ validations.any? { |v| v.is_a?(ActiveModel::Validations::PresenceValidator) }
18
+ end.map(&:first) # get fields that are invalid
19
+ # Permalinks presence is validated, but are really automatically generated
20
+ # Therefore we shouldn't tell API clients that they MUST send one through
21
+ required_fields.map!(&:to_s).delete("permalink")
22
+ # Do not require slugs, either
23
+ required_fields.delete("slug")
24
+ required_fields
25
+ end
26
+
27
+ @@image_attributes = [
28
+ { image: [] }, :image_size, :image_title, :image_alt
29
+ ]
30
+
31
+ @@page_attributes = [
32
+ :browser_title, :draft, :link_url, :menu_title, :meta_description,
33
+ :parent_id, :skip_to_first_child, :show_in_menu, :title, :view_template,
34
+ :layout_template, :custom_slug, parts_attributes: [:id, :title, :slug, :body, :position]
35
+ ]
36
+
37
+ @@page_part_attributes = [
38
+ :title, :slug, :body, :locale
39
+ ]
40
+
41
+ @@resource_attributes = [
42
+ :resource_title, { file: [] }
43
+ ]
44
+
45
+ @@blog_post_attributes = [
46
+ :title, :body, :custom_teaser, :tag_list,
47
+ :draft, :published_at, :custom_url, :user_id, :username, :browser_title,
48
+ :meta_description, :source_url, :source_url_title, category_ids: []
49
+ ]
50
+
51
+ @@inquiries_inquiry_attributes = [:name, :phone, :message, :email]
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,19 @@
1
+ module Refinery
2
+ module UserApiAuthentication
3
+ def generate_refinery_api_key!
4
+ self.refinery_api_key = generate_refinery_api_key
5
+ save!
6
+ end
7
+
8
+ def clear_refinery_api_key!
9
+ self.refinery_api_key = nil
10
+ save!
11
+ end
12
+
13
+ private
14
+
15
+ def generate_refinery_api_key
16
+ SecureRandom.hex(24)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,59 @@
1
+ # Implementation class for Cancan gem. Instead of overriding this class, consider adding new permissions
2
+ # using the special +register_ability+ method which allows extensions to add their own abilities.
3
+ #
4
+ # See http://github.com/ryanb/cancan for more details on cancan.
5
+ require 'cancan'
6
+ module Refinery
7
+ class Ability
8
+ include CanCan::Ability
9
+
10
+ class_attribute :abilities
11
+ self.abilities = Set.new
12
+
13
+ # Allows us to go beyond the standard cancan initialize method which makes it difficult for engines to
14
+ # modify the default +Ability+ of an application. The +ability+ argument must be a class that includes
15
+ # the +CanCan::Ability+ module. The registered ability should behave properly as a stand-alone class
16
+ # and therefore should be easy to test in isolation.
17
+ def self.register_ability(ability)
18
+ self.abilities.add(ability)
19
+ end
20
+
21
+ def self.remove_ability(ability)
22
+ self.abilities.delete(ability)
23
+ end
24
+
25
+ def initialize(user)
26
+ self.clear_aliased_actions
27
+
28
+ # override cancan default aliasing (we don't want to differentiate between read and index)
29
+ alias_action :delete, to: :destroy
30
+ alias_action :edit, to: :update
31
+ alias_action :new, to: :create
32
+ alias_action :new_action, to: :create
33
+ alias_action :show, to: :read
34
+ alias_action :index, :read, to: :display
35
+ alias_action :create, :update, :destroy, to: :modify
36
+
37
+ user ||= Refinery::Api.user_class.new
38
+
39
+ if user.respond_to?(:has_role?) && user.has_role?('superuser')
40
+ can :manage, :all
41
+ else
42
+ can :display, Image
43
+ can :display, Page
44
+ can :display, Resource
45
+ can :display, Blog::Post if defined?(Refinery::Blog)
46
+ can :display, Inquiries::Inquiry if defined?(Refinery::Inquiries)
47
+ end
48
+
49
+ # Include any abilities registered by extensions, etc.
50
+ Ability.abilities.each do |clazz|
51
+ ability = clazz.send(:new, user)
52
+ @rules = rules + ability.send(:rules)
53
+ end
54
+
55
+ # Protect admin role
56
+ cannot [:update, :destroy], Role, name: ['superuser'] if defined?(Role)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,2 @@
1
+ object false
2
+ node(:error) { I18n.t(:gateway_error, scope: "refinery.api", text: @error) }
@@ -0,0 +1,2 @@
1
+ object false
2
+ node(:error) { I18n.t(:invalid_api_key, :key => api_key, :scope => "refinery.api") }
@@ -0,0 +1,3 @@
1
+ object false
2
+ node(:error) { I18n.t(:invalid_resource, :scope => "refinery.api") }
3
+ node(:errors) { @resource.errors.to_hash }
@@ -0,0 +1,2 @@
1
+ object false
2
+ node(:error) { I18n.t(:must_specify_api_key, :scope => "refinery.api") }
@@ -0,0 +1,2 @@
1
+ object false
2
+ node(:error) { I18n.t(:resource_not_found, :scope => "refinery.api") }
@@ -0,0 +1,2 @@
1
+ object false
2
+ node(:error) { I18n.t(:unauthorized, :scope => "refinery.api") }
@@ -0,0 +1,5 @@
1
+ object false
2
+ node(:count) { @posts.count }
3
+ child @posts => :posts do
4
+ extends "refinery/api/v1/blog/posts/show"
5
+ end
@@ -0,0 +1,3 @@
1
+ object false
2
+ node(:attributes) { [*blog_post_attributes] }
3
+ node(:required_attributes) { required_fields_for(Refinery::Blog::Post) }
@@ -0,0 +1,5 @@
1
+ object @post
2
+
3
+ cache [I18n.locale, 'show', root_object]
4
+
5
+ attributes *blog_post_attributes
@@ -0,0 +1,5 @@
1
+ object false
2
+ node(:count) { @images.count }
3
+ child(@images => :images) do
4
+ extends "refinery/api/v1/images/show"
5
+ end
@@ -0,0 +1,3 @@
1
+ object false
2
+ node(:attributes) { [*image_attributes] }
3
+ node(:required_attributes) { required_fields_for(Refinery::Image) }
@@ -0,0 +1,6 @@
1
+ object @image
2
+ attributes :image_size, :image_title, :image_alt
3
+
4
+ node :image do |image|
5
+ image.image.url
6
+ end
@@ -0,0 +1,5 @@
1
+ object false
2
+ node(:count) { @inquiries.count }
3
+ child @inquiries => :inquiries do
4
+ extends "refinery/api/v1/inquiries/inquiries/show"
5
+ end
@@ -0,0 +1,3 @@
1
+ object false
2
+ node(:attributes) { [*inquiries_inquiry_attributes] }
3
+ node(:required_attributes) { required_fields_for(Refinery::Inquiries::Inquiry) }
@@ -0,0 +1,5 @@
1
+ object @inquiry
2
+
3
+ cache [I18n.locale, 'show', root_object]
4
+
5
+ attributes *inquiries_inquiry_attributes
@@ -0,0 +1,8 @@
1
+ object false
2
+ node(:count) { @pages.count }
3
+ child @pages => :pages do
4
+ attributes *page_attributes
5
+ unless params[:without_children]
6
+ extends "refinery/api/v1/pages/pages"
7
+ end
8
+ end