refinerycms-api 1.0.0.beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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