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,3 @@
1
+ object false
2
+ node(:attributes) { [*page_attributes] }
3
+ node(:required_attributes) { required_fields_for(Refinery::Page) }
@@ -0,0 +1,5 @@
1
+ attributes *page_attributes
2
+
3
+ node :pages do |t|
4
+ t.children.map { |c| partial("refinery/api/v1/pages/pages", :object => c) }
5
+ end
@@ -0,0 +1,9 @@
1
+ object @page
2
+
3
+ cache [I18n.locale, 'show', root_object]
4
+
5
+ attributes *page_attributes
6
+
7
+ child :parts => :page_parts do
8
+ attributes *page_part_attributes
9
+ end
@@ -0,0 +1,5 @@
1
+ object false
2
+ node(:count) { @resources.count }
3
+ child(@resources => :resources) do
4
+ extends "refinery/api/v1/resources/show"
5
+ end
@@ -0,0 +1,3 @@
1
+ object false
2
+ node(:attributes) { [*resource_attributes] }
3
+ node(:required_attributes) { required_fields_for(Refinery::Resource) }
@@ -0,0 +1,6 @@
1
+ object @resource
2
+ attributes :resource_title
3
+
4
+ node :file do |file|
5
+ file.file.url
6
+ end
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ ENGINE_PATH = File.expand_path('../..', __FILE__)
5
+ load File.expand_path('../../spec/dummy/bin/rails', __FILE__)
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rake' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+ begin
9
+ if Dir.exist?(File.expand_path('../../spec/dummy', __FILE__))
10
+ load File.expand_path("../spring", __FILE__)
11
+ end
12
+ rescue LoadError
13
+ end
14
+ require 'pathname'
15
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
16
+ Pathname.new(__FILE__).realpath)
17
+
18
+ require 'rubygems'
19
+ require 'bundler/setup'
20
+
21
+ load Gem.bin_path('rake', 'rake')
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ begin
10
+ if Dir.exist?(File.expand_path('../../spec/dummy', __FILE__))
11
+ load File.expand_path("../spring", __FILE__)
12
+ end
13
+ rescue LoadError
14
+ end
15
+ require 'pathname'
16
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
17
+ Pathname.new(__FILE__).realpath)
18
+
19
+ require 'rubygems'
20
+ require 'bundler/setup'
21
+
22
+ load Gem.bin_path('rspec-core', 'rspec')
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This file loads spring without using Bundler, in order to be fast
4
+ # It gets overwritten when you run the `spring binstub` command
5
+
6
+ unless defined?(Spring)
7
+ require "rubygems"
8
+ require "bundler"
9
+
10
+ if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)
11
+ ENV["GEM_PATH"] = ([Bundler.bundle_path.to_s] + Gem.path).join(File::PATH_SEPARATOR)
12
+ ENV["GEM_HOME"] = ""
13
+ Gem.paths = ENV
14
+
15
+ gem "spring", match[1]
16
+ require "spring/binstub"
17
+ end
18
+ end
@@ -0,0 +1 @@
1
+ Refinery::Api::BaseController.append_view_path(ApplicationController.view_paths)
@@ -0,0 +1,27 @@
1
+ en:
2
+ refinery:
3
+ api:
4
+ must_specify_api_key: "You must specify an API key."
5
+ invalid_api_key: "Invalid API key (%{key}) specified."
6
+ unauthorized: "You are not authorized to perform that action."
7
+ invalid_resource: "Invalid resource. Please fix errors and try again."
8
+ resource_not_found: "The resource you were looking for could not be found."
9
+ gateway_error: "There was a problem with the payment gateway: %{text}"
10
+ access: "API Access"
11
+ key: "Key"
12
+ clear_key: "Clear key"
13
+ regenerate_key: "Regenerate Key"
14
+ no_key: "No key"
15
+ generate_key: "Generate API key"
16
+ key_generated: "Key generated"
17
+ key_cleared: "Key cleared"
18
+ order:
19
+ could_not_transition: "The order could not be transitioned. Please fix the errors and try again."
20
+ invalid_shipping_method: "Invalid shipping method specified."
21
+ payment:
22
+ credit_over_limit: "This payment can only be credited up to %{limit}. Please specify an amount less than or equal to this number."
23
+ update_forbidden: "This payment cannot be updated because it is %{state}."
24
+ shipment:
25
+ cannot_ready: "Cannot ready shipment."
26
+ stock_location_required: "A stock_location_id parameter must be provided in order to retrieve stock movements."
27
+ invalid_taxonomy_id: "Invalid taxonomy id."
@@ -0,0 +1,24 @@
1
+ Refinery::Core::Engine.routes.draw do
2
+ namespace :api, defaults: { format: 'json' } do
3
+ namespace :v1 do
4
+ resources :images
5
+
6
+ resources :pages do
7
+ resources :page_parts
8
+ end
9
+
10
+ resources :resources
11
+
12
+ namespace :blog do
13
+ resources :posts
14
+ end
15
+
16
+ namespace :inquiries do
17
+ resources :inquiries, only: [:new, :create, :index, :show, :destroy]
18
+ end
19
+ end
20
+
21
+ match 'v:api/*path', to: redirect("/api/v1/%{path}"), via: [:get, :post, :put, :patch, :delete]
22
+ match '*path', to: redirect("/api/v1/%{path}"), via: [:get, :post, :put, :patch, :delete]
23
+ end
24
+ end
@@ -0,0 +1,8 @@
1
+ class AddApiKeyToRefineryAuthenticationDeviseUsers < ActiveRecord::Migration
2
+ def change
3
+ unless defined?(User)
4
+ add_column :refinery_authentication_devise_users, :refinery_api_key, :string, limit: 48
5
+ add_index :refinery_authentication_devise_users, :refinery_api_key
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,16 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/base'
3
+
4
+ module Refinery
5
+ class ApiGenerator < Rails::Generators::Base
6
+ source_root File.expand_path('../templates', __FILE__)
7
+
8
+ def rake_db
9
+ rake "refinery_api:install:migrations"
10
+ end
11
+
12
+ def generate_api_initializer
13
+ template 'config/initializers/refinery/api.rb.erb', File.join(destination_root, 'config', 'initializers', 'refinery', 'api.rb')
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ Refinery::Api.configure do |config|
2
+ # Requires authentication to access API
3
+ # config.requires_authentication = <%= Refinery::Api.requires_authentication.inspect %>
4
+
5
+ # If you're grafting onto an existing app, change this to your User class
6
+ # config.user_class = <%= Refinery::Api.user_class.to_s.inspect %>
7
+ end
@@ -0,0 +1,29 @@
1
+ require 'refinerycms-core'
2
+ require 'cancan'
3
+ require 'responders'
4
+ require 'rabl'
5
+
6
+ module Refinery
7
+ autoload :ApiGenerator, 'generators/refinery/api/api_generator'
8
+
9
+ module Api
10
+ require 'refinery/api/engine'
11
+ require 'refinery/api/configuration'
12
+
13
+ autoload :Version, 'refinery/api/version'
14
+
15
+ class GatewayError < RuntimeError; end
16
+
17
+ class << self
18
+ attr_writer :root
19
+
20
+ def root
21
+ @root ||= Pathname.new(File.expand_path('../../../', __FILE__))
22
+ end
23
+
24
+ def factory_paths
25
+ @factory_paths ||= [ root.join("spec/factories").to_s ]
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,32 @@
1
+ module Refinery
2
+ module Api
3
+ include ActiveSupport::Configurable
4
+
5
+ config_accessor :requires_authentication
6
+
7
+ self.requires_authentication = true
8
+
9
+ # Refinery::User isn't available when this line gets hit, so we use static methods instead
10
+ @@user_class_name = nil
11
+ class << self
12
+ def user_class=(class_name)
13
+ if class_name.is_a?(Class)
14
+ raise TypeError, "You can't set user_class to be a class, e.g., User. Instead, please use a string like 'User'"
15
+ elsif class_name.is_a?(String)
16
+ @@user_class_name = class_name
17
+ else
18
+ raise TypeError, "Invalid type for user_class. Please use a string like 'User'"
19
+ end
20
+ end
21
+
22
+ def user_class
23
+ class_name = @@user_class_name || "Refinery::Authentication::Devise::User"
24
+ begin
25
+ Object.const_get(class_name)
26
+ rescue NameError
27
+ class_name.constantize
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,76 @@
1
+ module Refinery
2
+ module Api
3
+ module ControllerHelpers
4
+ module Auth
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ helper_method :try_refinery_current_user
9
+
10
+ rescue_from CanCan::AccessDenied do |exception|
11
+ redirect_unauthorized_access
12
+ end
13
+ end
14
+
15
+ # Needs to be overriden so that we use Refinery's Ability rather than anyone else's.
16
+ def current_ability
17
+ @current_ability ||= Refinery::Ability.new(try_refinery_current_user)
18
+ end
19
+
20
+ def redirect_back_or_default(default)
21
+ redirect_to(session["refinery_user_return_to"] || request.env["HTTP_REFERER"] || default)
22
+ session["refinery_user_return_to"] = nil
23
+ end
24
+
25
+ def store_location
26
+ # disallow return to login, logout, signup pages
27
+ authentication_routes = [:refinery_signup_path, :refinery_login_path, :refinery_logout_path]
28
+ disallowed_urls = []
29
+ authentication_routes.each do |route|
30
+ if respond_to?(route)
31
+ disallowed_urls << send(route)
32
+ end
33
+ end
34
+
35
+ disallowed_urls.map!{ |url| url[/\/\w+$/] }
36
+ unless disallowed_urls.include?(request.fullpath)
37
+ session['refinery_user_return_to'] = request.fullpath.gsub('//', '/')
38
+ end
39
+ end
40
+
41
+ # proxy method to *possible* refinery_current_user method
42
+ # Authentication extensions (such as refinery_auth_devise) are meant to provide refinery_current_user
43
+ def try_refinery_current_user
44
+ # This one will be defined by apps looking to hook into Refinery
45
+ # As per authentication_helpers.rb
46
+ if respond_to?(:refinery_current_user)
47
+ refinery_current_user
48
+ # This one will be defined by Devise
49
+ elsif respond_to?(:current_refinery_user)
50
+ current_refinery_user
51
+ else
52
+ nil
53
+ end
54
+ end
55
+
56
+ # Redirect as appropriate when an access request fails. The default action is to redirect to the login screen.
57
+ # Override this method in your controllers if you want to have special behavior in case the user is not authorized
58
+ # to access the requested action. For example, a popup window might simply close itself.
59
+ def redirect_unauthorized_access
60
+ if try_refinery_current_user
61
+ flash[:error] = Refinery.t(:authorization_failure)
62
+ redirect_to refinery.forbidden_path
63
+ else
64
+ store_location
65
+ if respond_to?(:refinery_login_path)
66
+ redirect_to refinery_login_path
67
+ else
68
+ redirect_to refinery.respond_to?(:root_path) ? refinery.root_path : main_app.root_path
69
+ end
70
+ end
71
+ end
72
+
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,37 @@
1
+ module Refinery
2
+ module Api
3
+ module ControllerHelpers
4
+ module StrongParameters
5
+ def permitted_attributes
6
+ Refinery::PermittedAttributes
7
+ end
8
+
9
+ delegate *Refinery::PermittedAttributes::ATTRIBUTES,
10
+ to: :permitted_attributes,
11
+ prefix: :permitted
12
+
13
+ def permitted_image_attributes
14
+ permitted_attributes.image_attributes
15
+ end
16
+
17
+ def permitted_page_attributes
18
+ permitted_attributes.page_attributes + [
19
+ page_parts_attributes: permitted_page_part_attributes
20
+ ]
21
+ end
22
+
23
+ def permitted_resource_attributes
24
+ permitted_attributes.resource_attributes
25
+ end
26
+
27
+ def permitted_blog_post_attributes
28
+ permitted_attributes.blog_post_attributes
29
+ end
30
+
31
+ def permitted_inquiries_inquiry_attributes
32
+ permitted_attributes.inquiries_inquiry_attributes
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,20 @@
1
+ require 'refinery/api/responders'
2
+
3
+ module Refinery
4
+ module Api
5
+ module ControllerSetup
6
+ def self.included(klass)
7
+ klass.class_eval do
8
+ include CanCan::ControllerAdditions
9
+ include Refinery::Api::ControllerHelpers::Auth
10
+
11
+ prepend_view_path Rails.root + "app/views"
12
+ append_view_path File.expand_path("../../../app/views", File.dirname(__FILE__))
13
+
14
+ self.responder = Refinery::Api::Responders::AppResponder
15
+ respond_to :json
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,52 @@
1
+ require 'versioncake'
2
+
3
+ module Refinery
4
+ module Api
5
+ class Engine < Rails::Engine
6
+ include Refinery::Engine
7
+
8
+ isolate_namespace Refinery::Api
9
+ engine_name :refinery_api
10
+
11
+ config.autoload_paths += %W( #{config.root}/lib )
12
+
13
+ config.before_configuration do
14
+ ::Rabl.configure do |config|
15
+ config.include_json_root = false
16
+ config.include_child_root = false
17
+
18
+ # Motivation here it make it call as_json when rendering timestamps
19
+ # and therefore display miliseconds. Otherwise it would fall to
20
+ # JSON.dump which doesn't display the miliseconds
21
+ config.json_engine = ActiveSupport::JSON
22
+ end
23
+
24
+ config.versioncake.supported_version_numbers = [1]
25
+ config.versioncake.extraction_strategy = :http_header
26
+ end
27
+
28
+ config.after_initialize do
29
+ Refinery.register_engine(Refinery::Api)
30
+ end
31
+
32
+ # initializer "refinery.api.environment", :before => :load_config_initializers do |app|
33
+ # # Refinery::Api::Config = Refinery::ApiConfiguration.new
34
+ # end
35
+
36
+ # def self.activate
37
+ # Dir.glob(File.join(File.dirname(__FILE__), "../../../app/**/*_decorator*.rb")) do |c|
38
+ # Rails.configuration.cache_classes ? require(c) : load(c)
39
+ # end
40
+ # end
41
+ # config.to_prepare &method(:activate).to_proc
42
+
43
+ config.to_prepare do
44
+ Decorators.register! ::Refinery::Api.root
45
+ end
46
+
47
+ # def self.root
48
+ # @root ||= Pathname.new(File.expand_path('../../../../', __FILE__))
49
+ # end
50
+ end
51
+ end
52
+ end