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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fea548db2f08cdcac03566e7f651fbdd8251c017
4
+ data.tar.gz: ea1815014f6903f77300cf2e726655a4aa4152c9
5
+ SHA512:
6
+ metadata.gz: 636256ea9c45a20c3bb6c43f42e93f9db932135bb32ba1026c5724fe778e0494b003f0d7f97f8abf48055345a74dbb04ebb7c987a5c106afe947bcad007128a8
7
+ data.tar.gz: f3cf461aa73fa93a41c023913ce9c103ed7b7b811908a356b5e9d6375ed3a858d0496b5fe5ba9713b10530243d67a5520fd21435346d552b3e63356d39d23cd5
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .DS_Store
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ .gems
20
+
21
+ spec/dummy
@@ -0,0 +1 @@
1
+ .gems
@@ -0,0 +1 @@
1
+ 2.2.1
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ cache: bundler
3
+ bundler_args: --without development
4
+ before_script: "bin/rake refinery:testing:dummy_app"
5
+ env:
6
+ - DB=postgresql
7
+ - DB=mysql
8
+ rvm:
9
+ - 2.3.0
10
+ - 2.2
11
+ - 2.1
12
+ sudo: false
data/Gemfile ADDED
@@ -0,0 +1,57 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "refinerycms-authentication-devise", '~> 1.0.4'
4
+ gem "refinerycms-blog", github: 'refinery/refinerycms-blog', branch: 'bugfix/decoupling'
5
+ gem "refinerycms-inquiries", github: 'refinery/refinerycms-inquiries', branch: 'master'
6
+
7
+ gemspec
8
+
9
+ git "https://github.com/refinery/refinerycms", branch: "master" do
10
+ gem "refinerycms"
11
+
12
+ group :test do
13
+ gem "refinerycms-testing"
14
+ end
15
+ end
16
+
17
+
18
+
19
+ # Database Configuration
20
+ unless ENV["TRAVIS"]
21
+ gem "activerecord-jdbcsqlite3-adapter", :platform => :jruby
22
+ gem "sqlite3", :platform => :ruby
23
+ end
24
+
25
+ if !ENV["TRAVIS"] || ENV["DB"] == "mysql"
26
+ gem "activerecord-jdbcmysql-adapter", :platform => :jruby
27
+ gem "jdbc-mysql", "= 5.1.13", :platform => :jruby
28
+ gem "mysql2", :platform => :ruby
29
+ end
30
+
31
+ if !ENV["TRAVIS"] || ENV["DB"] == "postgresql"
32
+ gem "activerecord-jdbcpostgresql-adapter", :platform => :jruby
33
+ gem "pg", :platform => :ruby
34
+ end
35
+
36
+ gem "jruby-openssl", :platform => :jruby
37
+
38
+ # Refinery/rails should pull in the proper versions of these
39
+ group :assets do
40
+ gem "sass-rails"
41
+ gem "coffee-rails"
42
+ gem "uglifier"
43
+ end
44
+
45
+ group :development do
46
+ gem 'quiet_assets'
47
+ end
48
+
49
+ group :test do
50
+ gem "launchy"
51
+ gem 'rspec-activemodel-mocks', '~> 1.0.2'
52
+ end
53
+
54
+ # Load local gems according to Refinery developer preference.
55
+ if File.exist? local_gemfile = File.expand_path("../.gemfile", __FILE__)
56
+ eval File.read(local_gemfile)
57
+ end
data/LICENSE ADDED
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2007-2015, Spree Commerce, Inc. and other contributors
2
+ Copyright (c) 2016, Brice Sanchez and other contributors
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without modification,
6
+ are permitted provided that the following conditions are met:
7
+
8
+ * Redistributions of source code must retain the above copyright notice,
9
+ this list of conditions and the following disclaimer.
10
+ * Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+ * Neither the name Spree nor the names of its contributors may be used to
14
+ endorse or promote products derived from this software without specific
15
+ prior written permission.
16
+
17
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+
8
+ ENGINE_PATH = File.dirname(__FILE__)
9
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
10
+
11
+ if File.exists?(APP_RAKEFILE)
12
+ load 'rails/tasks/engine.rake'
13
+ end
14
+
15
+ require "refinerycms-testing"
16
+ Refinery::Testing::Railtie.load_dummy_tasks(ENGINE_PATH)
17
+
18
+ load File.expand_path('../tasks/rspec.rake', __FILE__)
19
+ load File.expand_path('../tasks/refinery_api.rake', __FILE__)
20
+
21
+ task :default => :spec
@@ -0,0 +1,174 @@
1
+ require_dependency 'refinery/api/controller_setup'
2
+
3
+ module Refinery
4
+ module Api
5
+ class BaseController < ActionController::Base
6
+ include Refinery::Api::ControllerSetup
7
+ include Refinery::Api::ControllerHelpers::StrongParameters
8
+
9
+ attr_accessor :current_api_user
10
+
11
+ before_action :set_content_type
12
+ before_action :load_user
13
+ before_action :authorize_for_order, if: Proc.new { order_token.present? }
14
+ before_action :authenticate_user
15
+ before_action :load_user_roles
16
+
17
+ rescue_from ActionController::ParameterMissing, with: :error_during_processing
18
+ rescue_from ActiveRecord::RecordInvalid, with: :error_during_processing
19
+ rescue_from ActiveRecord::RecordNotFound, with: :not_found
20
+ rescue_from CanCan::AccessDenied, with: :unauthorized
21
+ rescue_from Refinery::Api::GatewayError, with: :gateway_error
22
+
23
+ helper Refinery::Api::ApiHelpers
24
+
25
+ def map_nested_attributes_keys(klass, attributes)
26
+ nested_keys = klass.nested_attributes_options.keys
27
+ attributes.inject({}) do |h, (k,v)|
28
+ key = nested_keys.include?(k.to_sym) ? "#{k}_attributes" : k
29
+ h[key] = v
30
+ h
31
+ end.with_indifferent_access
32
+ end
33
+
34
+ # users should be able to set price when importing orders via api
35
+ def permitted_line_item_attributes
36
+ if @current_user_roles.include?("admin")
37
+ super + [:price, :variant_id, :sku]
38
+ else
39
+ super
40
+ end
41
+ end
42
+
43
+ def content_type
44
+ case params[:format]
45
+ when "json"
46
+ "application/json; charset=utf-8"
47
+ when "xml"
48
+ "text/xml; charset=utf-8"
49
+ end
50
+ end
51
+
52
+ protected
53
+
54
+ def authorisation_manager
55
+ @authorisation_manager ||= ::Refinery::Core::AuthorisationManager.new
56
+ end
57
+ # We ❤ you, too ️
58
+ alias_method :authorization_manager, :authorisation_manager
59
+
60
+ private
61
+
62
+ def set_content_type
63
+ headers["Content-Type"] = content_type
64
+ end
65
+
66
+ def load_user
67
+ @current_api_user = Refinery::Api.user_class.find_by(refinery_api_key: api_key.to_s)
68
+ end
69
+
70
+ def authenticate_user
71
+ return if @current_api_user
72
+
73
+ if requires_authentication? && api_key.blank? && order_token.blank?
74
+ render "refinery/api/errors/must_specify_api_key", status: 401 and return
75
+ elsif order_token.blank? && (requires_authentication? || api_key.present?)
76
+ render "refinery/api/errors/invalid_api_key", status: 401 and return
77
+ else
78
+ # An anonymous user
79
+ @current_api_user = Refinery::Api.user_class.new
80
+ end
81
+ end
82
+
83
+ def load_user_roles
84
+ @current_user_roles = @current_api_user ? @current_api_user.roles.pluck(:title) : []
85
+ end
86
+
87
+ def unauthorized
88
+ render "refinery/api/errors/unauthorized", status: 401 and return
89
+ end
90
+
91
+ def error_during_processing(exception)
92
+ Rails.logger.error exception.message
93
+ Rails.logger.error exception.backtrace.join("\n")
94
+
95
+ unprocessable_entity(exception.message)
96
+ end
97
+
98
+ def unprocessable_entity(message)
99
+ render text: { exception: message }.to_json, status: 422
100
+ end
101
+
102
+ def gateway_error(exception)
103
+ @order.errors.add(:base, exception.message)
104
+ invalid_resource!(@order)
105
+ end
106
+
107
+ def requires_authentication?
108
+ Refinery::Api.requires_authentication
109
+ end
110
+
111
+ def not_found
112
+ render "refinery/api/errors/not_found", status: 404 and return
113
+ end
114
+
115
+ def current_ability
116
+ Refinery::Ability.new(current_api_user)
117
+ end
118
+
119
+ def invalid_resource!(resource)
120
+ @resource = resource
121
+ render "refinery/api/errors/invalid_resource", status: 422
122
+ end
123
+
124
+ def api_key
125
+ request.headers["X-Refinery-Token"] || params[:token]
126
+ end
127
+ helper_method :api_key
128
+
129
+ def order_token
130
+ request.headers["X-Refinery-Order-Token"] || params[:order_token]
131
+ end
132
+
133
+ def find_product(id)
134
+ product_scope.friendly.find(id.to_s)
135
+ rescue ActiveRecord::RecordNotFound
136
+ product_scope.find(id)
137
+ end
138
+
139
+ def product_scope
140
+ if @current_user_roles.include?("admin")
141
+ scope = Product.with_deleted.accessible_by(current_ability, :read).includes(*product_includes)
142
+
143
+ unless params[:show_deleted]
144
+ scope = scope.not_deleted
145
+ end
146
+ unless params[:show_discontinued]
147
+ scope = scope.not_discontinued
148
+ end
149
+ else
150
+ scope = Product.accessible_by(current_ability, :read).active.includes(*product_includes)
151
+ end
152
+
153
+ scope
154
+ end
155
+
156
+ def variants_associations
157
+ [{ option_values: :option_type }, :default_price, :images]
158
+ end
159
+
160
+ def product_includes
161
+ [:option_types, :taxons, product_properties: :property, variants: variants_associations, master: variants_associations]
162
+ end
163
+
164
+ def order_id
165
+ params[:order_id] || params[:checkout_id] || params[:order_number]
166
+ end
167
+
168
+ def authorize_for_order
169
+ @order = Refinery::Order.find_by(number: order_id)
170
+ authorize! :read, @order, order_token
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,78 @@
1
+ if defined?(Refinery::Blog)
2
+ module Refinery
3
+ module Api
4
+ module V1
5
+ module Blog
6
+ class PostsController < Refinery::Api::BaseController
7
+
8
+ def index
9
+ if params[:ids]
10
+ @posts = Refinery::Blog::Post.
11
+ accessible_by(current_ability, :read).
12
+ where(id: params[:ids].split(','))
13
+ else
14
+ @posts = Refinery::Blog::Post.
15
+ accessible_by(current_ability, :read).
16
+ # ransack(params[:q]).result
17
+ newest_first
18
+ end
19
+
20
+ respond_with(@posts)
21
+ end
22
+
23
+ def show
24
+ @post = post
25
+ respond_with(@post)
26
+ end
27
+
28
+ def new
29
+ end
30
+
31
+ def create
32
+ authorize! :create, ::Refinery::Blog::Post
33
+ @post = Refinery::Blog::Post.new(post_params)
34
+
35
+ if @post.save
36
+ respond_with(@post, status: 201, default_template: :show)
37
+ else
38
+ invalid_resource!(@post)
39
+ end
40
+ end
41
+
42
+ def update
43
+ authorize! :update, post
44
+ if post.update_attributes(post_params)
45
+ respond_with(post, status: 200, default_template: :show)
46
+ else
47
+ invalid_resource!(post)
48
+ end
49
+ end
50
+
51
+ def destroy
52
+ authorize! :destroy, post
53
+ post.destroy
54
+ respond_with(post, status: 204)
55
+ end
56
+
57
+ private
58
+
59
+ def post
60
+ @post ||= Refinery::Blog::Post.
61
+ accessible_by(current_ability, :read).
62
+ find(params[:id])
63
+ end
64
+
65
+ def post_params
66
+ if params[:post] && !params[:post].empty?
67
+ params.require(:post).permit(permitted_blog_post_attributes)
68
+ else
69
+ {}
70
+ end
71
+ end
72
+
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,76 @@
1
+ module Refinery
2
+ module Api
3
+ module V1
4
+ class ImagesController < Refinery::Api::BaseController
5
+ def index
6
+ @images = Refinery::Image.
7
+ includes(:translations).
8
+ accessible_by(current_ability, :read)
9
+ respond_with(@images)
10
+ end
11
+
12
+ def show
13
+ @image = Refinery::Image.
14
+ includes(:translations).
15
+ accessible_by(current_ability, :read).
16
+ find(params[:id])
17
+ respond_with(@image)
18
+ end
19
+
20
+ def new
21
+ end
22
+
23
+ def create
24
+ authorize! :create, Image
25
+ @images = []
26
+ begin
27
+ if params[:image].present? && params[:image][:image].is_a?(Array)
28
+ params[:image][:image].each do |image|
29
+ params[:image][:image_title] = params[:image][:image_title].presence || auto_title(image.original_filename)
30
+ @images << (@image = ::Refinery::Image.create({image: image}.merge(image_params.except(:image))))
31
+ end
32
+ else
33
+ @images << (@image = ::Refinery::Image.create(image_params))
34
+ end
35
+ rescue NotImplementedError
36
+ logger.warn($!.message)
37
+ @image = ::Refinery::Image.new
38
+ end
39
+
40
+ if @images.all?(&:valid?)
41
+ respond_with(@images, status: 201, default_template: :show)
42
+ else
43
+ invalid_resource!(@images)
44
+ end
45
+ end
46
+
47
+ def update
48
+ @image = Refinery::Image.accessible_by(current_ability, :update).find(params[:id])
49
+ if @image.update_attributes(image_params)
50
+ respond_with(@image, default_template: :show)
51
+ else
52
+ invalid_resource!(@image)
53
+ end
54
+ end
55
+
56
+ def destroy
57
+ @image = Refinery::Image.accessible_by(current_ability, :destroy).find(params[:id])
58
+ @image.destroy
59
+ respond_with(@image, status: 204)
60
+ end
61
+
62
+ protected
63
+
64
+ def auto_title(filename)
65
+ CGI::unescape(filename.to_s).gsub(/\.\w+$/, '').titleize
66
+ end
67
+
68
+ private
69
+
70
+ def image_params
71
+ params.require(:image).permit(permitted_image_attributes)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end