openstax_api 2.5.1 → 3.0.0

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +4 -6
  3. data/app/controllers/openstax/api/v1/api_controller.rb +1 -1
  4. data/app/representers/openstax/api/v1/abstract_search_representer.rb +40 -0
  5. data/lib/openstax/api/constraints.rb +3 -4
  6. data/lib/openstax/api/engine.rb +3 -8
  7. data/lib/openstax/api/representable_schema_printer.rb +58 -35
  8. data/lib/openstax/api/roar.rb +50 -93
  9. data/lib/openstax/api/version.rb +1 -1
  10. data/spec/controllers/openstax/api/v1/api_controller_spec.rb +88 -2
  11. data/spec/dummy/README.md +1 -1
  12. data/spec/dummy/Rakefile +1 -2
  13. data/spec/dummy/app/assets/javascripts/application.js +3 -5
  14. data/spec/dummy/app/assets/stylesheets/application.css +5 -3
  15. data/spec/dummy/app/models/user.rb +2 -0
  16. data/spec/dummy/app/representers/user_representer.rb +13 -0
  17. data/spec/dummy/app/representers/user_search_representer.rb +5 -0
  18. data/spec/dummy/app/routines/search_users.rb +22 -0
  19. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  20. data/spec/dummy/bin/bundle +3 -0
  21. data/spec/dummy/bin/rails +4 -0
  22. data/spec/dummy/bin/rake +4 -0
  23. data/spec/dummy/config/application.rb +0 -30
  24. data/spec/dummy/config/boot.rb +4 -9
  25. data/spec/dummy/config/database.yml +8 -8
  26. data/spec/dummy/config/environment.rb +3 -3
  27. data/spec/dummy/config/environments/development.rb +21 -13
  28. data/spec/dummy/config/environments/production.rb +41 -32
  29. data/spec/dummy/config/environments/test.rb +17 -13
  30. data/spec/dummy/config/initializers/assets.rb +8 -0
  31. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  32. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  33. data/spec/dummy/config/initializers/inflections.rb +6 -5
  34. data/spec/dummy/config/initializers/mime_types.rb +0 -1
  35. data/spec/dummy/config/initializers/openstax_api.rb +2 -2
  36. data/spec/dummy/config/initializers/session_store.rb +1 -6
  37. data/spec/dummy/config/initializers/wrap_parameters.rb +6 -6
  38. data/spec/dummy/config/locales/en.yml +20 -2
  39. data/spec/dummy/config/secrets.yml +22 -0
  40. data/spec/dummy/config.ru +1 -1
  41. data/spec/dummy/db/development.sqlite3 +0 -0
  42. data/spec/dummy/db/migrate/1_create_users.rb +16 -0
  43. data/spec/dummy/db/schema.rb +38 -32
  44. data/spec/dummy/db/test.sqlite3 +0 -0
  45. data/spec/dummy/lib/controller_includes.rb +2 -6
  46. data/spec/dummy/log/development.log +486 -1445
  47. data/spec/dummy/log/test.log +16720 -3757
  48. data/spec/dummy/public/404.html +54 -13
  49. data/spec/dummy/public/422.html +54 -13
  50. data/spec/dummy/public/500.html +53 -12
  51. data/spec/factories/user.rb +8 -0
  52. data/spec/lib/openstax/api/apipie_spec.rb +15 -0
  53. data/spec/lib/openstax/api/constraints_spec.rb +6 -6
  54. data/spec/lib/openstax/api/doorkeeper_application_includes_spec.rb +1 -1
  55. data/spec/lib/openstax/api/representable_schema_printer_spec.rb +5 -3
  56. data/spec/lib/openstax/api/roar_spec.rb +22 -0
  57. data/spec/lib/openstax/api/routing_mapper_includes_spec.rb +1 -1
  58. data/spec/lib/openstax_api_spec.rb +19 -0
  59. data/spec/models/openstax/api/api_user_spec.rb +2 -2
  60. data/spec/rails_helper.rb +54 -0
  61. data/spec/representers/openstax/api/v1/abstract_search_representer_spec.rb +127 -0
  62. data/spec/spec_helper.rb +80 -13
  63. metadata +85 -18
  64. data/spec/dummy/app/models/dummy_user.rb +0 -2
  65. data/spec/dummy/app/representers/dummy_user_representer.rb +0 -9
  66. data/spec/dummy/config/initializers/secret_token.rb +0 -7
  67. data/spec/dummy/db/migrate/1_create_dummy_users.rb +0 -10
  68. data/spec/dummy/script/rails +0 -6
  69. data/spec/lib/openstax/api/apipie.rb +0 -11
  70. data/spec/lib/openstax/api/roar.rb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4e9f64fc680c9305cdab0cf7c5763750825793bc
4
- data.tar.gz: 0d469d8177f3417341d18e502ba8c575bc7bb153
3
+ metadata.gz: e4a4d7a6e7dd7a7ccda5ca535edb33ce7dc6694c
4
+ data.tar.gz: 1337c07dcb6b7097a7411850b09781ab0729110e
5
5
  SHA512:
6
- metadata.gz: acdfccc6cda3472e3f14f7cee2e2feb21175d5208719a0adbf42f8f4a368259b583f3dafa8012504e170e31f736826aa18f8a52ba5b2ee0c9b3bb13cdd15c6b7
7
- data.tar.gz: 1e9d2348a19c27782de4f31ed8740120b3ce6346e45ce8d452d56a6fb8cb91c3cf258dbb98a4c4e7df3889e4852b08e44f8ddc0b93df2d490d466ee4e450988e
6
+ metadata.gz: d7b974c2cb4bc39dea96d829e94b5296db85fa15d03e8d9cb30f075e27a3e606296ad3188a68cb6108d451a9ba322ba386087469e40d85718125af75ae57a609
7
+ data.tar.gz: dc0ee731484d0a14411327ee1cde7d0e622421d0973d47108255a11ab22a5104df0a3ee1975ffd14fa5e0d26a0ab158fd3d0c413023a435da40bb2644040a1e6
data/Rakefile CHANGED
@@ -11,12 +11,10 @@ load 'rails/tasks/engine.rake'
11
11
 
12
12
  Bundler::GemHelper.install_tasks
13
13
 
14
- require 'rake/testtask'
14
+ require 'rspec/core'
15
+ require 'rspec/core/rake_task'
15
16
 
16
- Rake::TestTask.new(:spec => 'app:db:test:prepare') do |t|
17
- t.libs << 'spec'
18
- t.pattern = 'spec/**/*_spec.rb'
19
- t.verbose = false
20
- end
17
+ desc 'Run all specs in spec directory (excluding plugin specs)'
18
+ RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
21
19
 
22
20
  task :default => :spec
@@ -37,7 +37,7 @@ module OpenStax
37
37
  protected
38
38
 
39
39
  def session_user?
40
- current_session_user && doorkeeper_token.blank?
40
+ !!current_session_user && doorkeeper_token.blank?
41
41
  end
42
42
 
43
43
  end
@@ -0,0 +1,40 @@
1
+ # Represents search results for a JSON API
2
+ #
3
+ # Subclasses should define the representer for the search results:
4
+ # collection :items, inherit: true, decorator: SomeRepresenter
5
+ #
6
+ # See spec/dummy/app/representers/user_search_representer.rb for an example search representer
7
+
8
+ module OpenStax
9
+ module Api
10
+ module V1
11
+ class AbstractSearchRepresenter < ::Roar::Decorator
12
+
13
+ include ::Roar::Representer::JSON
14
+
15
+ property :total_count,
16
+ type: Integer,
17
+ readable: true,
18
+ writeable: false,
19
+ exec_context: :decorator,
20
+ schema_info: {
21
+ required: true,
22
+ description: "The number of items matching the query; can be more than the number returned if paginating"
23
+ }
24
+
25
+ collection :items,
26
+ readable: true,
27
+ writeable: false,
28
+ schema_info: {
29
+ required: true,
30
+ description: "The items matching the query or a subset thereof when paginating"
31
+ }
32
+
33
+ def total_count
34
+ represented[:total_count] || represented[:items].limit(nil).offset(nil).count
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,16 +1,15 @@
1
+ require 'openstax_utilities'
2
+
1
3
  module OpenStax
2
4
  module Api
3
5
  class Constraints
4
- cattr_accessor :main_app_name
5
-
6
6
  def initialize(options)
7
7
  @version = options[:version]
8
8
  @default = options[:default]
9
9
  end
10
10
 
11
11
  def api_accept_header
12
- self.main_app_name ||= OpenStax::Api::Engine::MAIN_APP_NAME.underscore
13
- "application/vnd.#{self.main_app_name}.openstax.#{@version.to_s}"
12
+ "application/vnd.openstax.#{OSU::SITE_NAME}.#{@version.to_s}"
14
13
  end
15
14
 
16
15
  def matches?(req)
@@ -1,5 +1,6 @@
1
1
  require 'roar-rails'
2
2
  require 'exception_notification'
3
+ require 'openstax_utilities'
3
4
  require 'openstax/api/roar'
4
5
  require 'openstax/api/apipie'
5
6
 
@@ -8,17 +9,11 @@ module OpenStax
8
9
  class Engine < ::Rails::Engine
9
10
  isolate_namespace OpenStax::Api
10
11
 
11
- config.after_initialize do
12
- MAIN_APP_NAME = ::Rails.application.class.parent_name
13
- end
14
-
15
12
  config.generators do |g|
16
13
  g.test_framework :rspec, :fixture => false
17
14
  g.fixture_replacement :factory_girl, :dir => 'spec/factories'
18
- end
19
-
20
- ActiveSupport::Inflector.inflections do |inflect|
21
- inflect.acronym 'OpenStax'
15
+ g.assets false
16
+ g.helper false
22
17
  end
23
18
  end
24
19
  end
@@ -2,6 +2,7 @@ module OpenStax
2
2
  module Api
3
3
  class RepresentableSchemaPrinter
4
4
 
5
+ # Returns the json schema for a representer
5
6
  def self.json_schema(representer, options = {})
6
7
  definitions = {}
7
8
  schema = json_object(representer, definitions, options)
@@ -9,6 +10,8 @@ module OpenStax
9
10
  schema
10
11
  end
11
12
 
13
+ # Returns some formatted Markdown with HTML containing the
14
+ # JSON schema for a given representer
12
15
  def self.json(representer, options={})
13
16
  options[:include] ||= [:readable, :writeable]
14
17
 
@@ -20,29 +23,34 @@ module OpenStax
20
23
  "\n<pre class='code'>\n#{json_string}\n</pre>\n"
21
24
  end
22
25
 
23
- protected
26
+ protected
24
27
 
28
+ # Attempts to extract the given representer's name
25
29
  def self.representer_name(representer)
26
30
  name = representer.name
27
31
  return nil if name.nil?
28
32
  name.chomp('Representer').demodulize.camelize(:lower)
29
33
  end
30
34
 
35
+ # Gets the definition name for the given representer name
31
36
  def self.definition_name(name)
32
37
  "#/definitions/#{name}"
33
38
  end
34
39
 
40
+ # Helper function for json_schema
35
41
  def self.json_object(representer, definitions, options = {})
42
+ # Initialize the schema
36
43
  schema = { type: :object, required: [], properties: {},
37
44
  additionalProperties: false }
38
45
 
39
46
  representer.representable_attrs.each do |attr|
40
- name = attr[:as].evaluate(self)
47
+ # Handle some common attributes (as, schema_info, required)
48
+ name = attr[:as].evaluate(representer)
41
49
  schema_info = attr[:schema_info] || {}
42
50
 
43
51
  schema[:required].push(name.to_sym) if schema_info[:required]
44
52
 
45
- # Skip unless attr includes the specified key or is required
53
+ # Skip attr it attr includes the specified key or is "required"
46
54
  next unless [options[:include]].flatten.any?{ |inc|
47
55
  m = inc.to_s + "?"
48
56
  attr.respond_to?(m) ? attr.send(m) : attr[inc]
@@ -54,65 +62,79 @@ module OpenStax
54
62
  (name.end_with?('id') ? :integer : :string) : type
55
63
  attr_info ||= { type: type }
56
64
 
65
+ # Process the schema_info attribute
57
66
  schema_info.each do |key, value|
67
+ # Handle special keys (required, definitions, type)
58
68
  next if key == :required
59
69
  if key == :definitions
60
70
  definitions.merge!(value)
61
71
  next
62
72
  end
63
73
  value = value.to_s.downcase if key == :type
74
+ # Store the schema_info k-v pair in attr_info
64
75
  attr_info[key] = value
65
76
  end
66
77
 
67
78
  # Overwrite type for collections
68
79
  attr_info[:type] = 'array' if attr[:collection]
69
80
 
81
+ # Handle nested representers
70
82
  if attr[:extend]
71
- # We're dealing with a nested representer. It may just be a simple representer
72
- # or it could be an Uber::Callable (representing that the representer could be
73
- # one of a number of possible representers)
74
-
75
- if attr[:extend].is_a?(Uber::Options::Value) && attr[:extend].dynamic?
76
- # We're dealing with an Uber::Callable situation, so need to get the list of
77
- # possible representers and add each one to the "oneOf" list as well as to
78
- # the definitions hash
79
-
80
- attr_info[:type] = 'object'
81
- attr_info[:oneOf] = []
82
-
83
- attr[:extend].evaluate(:all_sub_representers).each do |sub_representer|
84
- srname = representer_name(sub_representer)
85
- attr_info[:oneOf].push(:$ref => definition_name(srname))
86
- definitions[srname] = json_object(sub_representer, definitions, options) if definitions[srname].nil?
87
- end
88
- else
89
- # We're dealing with a simple representer
90
-
91
- decorator = attr[:extend].evaluate(self)
83
+ # The nested representer can be either a simple representer or
84
+ # an Uber::Callable, in which case there could be multiple representers
85
+
86
+ # Get the nested representers
87
+ # Evaluate syntax is evaluate(context, instance or class, *args)
88
+ # We have no instance or class (since we have no fragment), so we pass nil
89
+ # By convention, the callables we use should return an array of all
90
+ # possible representers when we pass the :all_sub_representers => true option
91
+ decorators = [attr[:extend].evaluate(representer, nil, :all_sub_representers => true)].flatten
92
+
93
+ # Count the representers
94
+ include_oneof = decorators.length > 1
95
+ # If we have more than one possible representer, use oneOf to list them all
96
+ sreps = include_oneof ? { oneOf: [] } : {}
97
+
98
+ decorators.each do |decorator|
99
+ # Attempt to get the representer's name
92
100
  rname = representer_name(decorator)
93
101
 
94
102
  if rname
103
+ # We have a representer name, so use a schema definition with that name
95
104
  dname = definition_name(rname)
105
+ dhash = { :$ref => dname }
106
+
107
+ include_oneof ? sreps[:oneOf].push(dhash) : sreps = dhash
96
108
 
97
- if attr[:collection]
98
- attr_info[:items] = { :$ref => dname }
99
- else
100
- # Type is included in ref
101
- attr_info.delete(:type)
102
- attr_info[:$ref] = dname
103
- end
104
109
  if definitions[rname].nil?
110
+ # No definition with that name found, so add it
111
+ # A blank definition is added first to prevent infinite loops
105
112
  definitions[rname] = {}
106
- definitions[rname] = json_object(decorator,
107
- definitions, options)
113
+ definitions[rname] = json_object(decorator, definitions, options)
108
114
  end
109
115
  else
110
- attr_info.merge!(json_object(decorator, definitions, options))
116
+ # No representer name, so add the object inline instead
117
+ obj = json_object(decorator, definitions, options)
118
+ include_oneof ? sreps[:oneOf].push(obj) : sreps = obj
111
119
  end
112
- end # .is_a?(...)
120
+ end
121
+
122
+ if attr[:collection]
123
+ # Collection
124
+ # Type already set above (array)
125
+ # Add sub representers under :items
126
+ attr_info[:items] = sreps
127
+ else
128
+ # Not a collection
129
+ # Type included in ref
130
+ # Add sub representers inline
131
+ attr_info.delete(:type)
132
+ attr_info.merge!(sreps)
133
+ end
113
134
 
114
135
  end
115
136
 
137
+ # Merge attr_info back into the schema
116
138
  schema[:properties][name.to_sym] = attr_info
117
139
  end
118
140
 
@@ -121,6 +143,7 @@ module OpenStax
121
143
  schema.delete(field) if schema[field].blank?
122
144
  end
123
145
 
146
+ # Return the completed object schema
124
147
  schema
125
148
  end
126
149
 
@@ -8,43 +8,46 @@ module OpenStax
8
8
 
9
9
  module Roar
10
10
 
11
- def get_representer(represent_with, model=nil)
12
- return nil if represent_with.nil?
13
- if represent_with.is_a? Proc
14
- represent_with.call(model)
15
- else
16
- represent_with
17
- end
11
+ def standard_create(model, represent_with=nil, &block)
12
+ standard_nested_create(model, nil, nil, represent_with, &block)
18
13
  end
19
14
 
20
- def standard_read(model_klass, id, represent_with=nil)
21
- @model = model_klass.find(id)
15
+ def standard_read(model, represent_with=nil)
22
16
  OSU::AccessPolicy.require_action_allowed!(:read, current_api_user, @model)
23
- respond_with @model, represent_with: get_representer(represent_with, @model)
17
+ respond_with model, represent_with: represent_with
24
18
  end
25
19
 
26
- def standard_update(model_klass, id, represent_with=nil)
27
- @model = model_klass.find(id)
28
- OSU::AccessPolicy.require_action_allowed!(:update, current_api_user, @model)
29
- consume!(@model, represent_with: get_representer(represent_with, @model))
20
+ def standard_update(model, represent_with=nil)
21
+ OSU::AccessPolicy.require_action_allowed!(:update, current_api_user, model)
22
+
23
+ model_klass.transaction do
24
+ consume!(model, represent_with: represent_with)
25
+ yield model if block_given?
26
+ OSU::AccessPolicy.require_action_allowed!(:update, current_api_user, model)
30
27
 
31
- if @model.save
32
- head :no_content
33
- else
34
- render json: @model.errors, status: :unprocessable_entity
28
+ if model.save
29
+ head :no_content
30
+ else
31
+ render json: model.errors, status: :unprocessable_entity
32
+ end
35
33
  end
36
34
  end
37
35
 
38
- def standard_create(model_klass, represent_with=nil, &block)
39
- standard_nested_create(model_klass, nil, nil, represent_with, &block)
36
+ def standard_destroy(model)
37
+ OSU::AccessPolicy.require_action_allowed!(:destroy, current_api_user, model)
38
+
39
+ if model.destroy
40
+ head :no_content
41
+ else
42
+ render json: model.errors, status: :unprocessable_entity
43
+ end
40
44
  end
41
45
 
42
- def standard_nested_create(model_klass, container_association=nil, container_id=nil, represent_with=nil)
43
- @model = model_klass.new()
44
-
45
- if container_association && container_id
46
- foreign_key = model_klass.reflect_on_association(container_association).association_foreign_key
47
- @model.send(foreign_key + '=', container_id)
46
+ def standard_nested_create(model, container_association=nil,
47
+ container=nil, represent_with=nil)
48
+ if container_association && container
49
+ OSU::AccessPolicy.require_action_allowed!(:update, current_api_user, container)
50
+ model.send("#{container_association.to_s}=", container)
48
51
  end
49
52
 
50
53
  # Unlike the implications of the representable README, "consume!" can
@@ -52,80 +55,34 @@ module OpenStax
52
55
  # We do want to consume before checking the permissions so we can know
53
56
  # what we're dealing with, but if user doesn't have permission we don't
54
57
  # want to have changed the DB. Wrap in a transaction to protect ourselves.
55
-
56
58
  model_klass.transaction do
57
- consume!(@model, represent_with: get_representer(represent_with, @model))
58
- yield @model if block_given?
59
- OSU::AccessPolicy.require_action_allowed!(:create, current_api_user, @model)
60
- end
61
-
62
- if @model.save
63
- respond_with @model, represent_with: get_representer(represent_with, @model), status: :created
64
- else
65
- render json: @model.errors, status: :unprocessable_entity
59
+ consume!(model, represent_with: represent_with)
60
+ yield model if block_given?
61
+ OSU::AccessPolicy.require_action_allowed!(:create, current_api_user, model)
62
+
63
+ if model.save
64
+ respond_with model, represent_with: represent_with, status: :created
65
+ else
66
+ render json: model.errors, status: :unprocessable_entity
67
+ end
66
68
  end
67
69
  end
68
70
 
69
- def standard_destroy(model_klass, id)
70
- @model = model_klass.find(id)
71
- OSU::AccessPolicy.require_action_allowed!(:destroy, current_api_user, @model)
72
-
73
- if @model.destroy
74
- head :no_content
75
- else
76
- render json: @model.errors, status: :unprocessable_entity
77
- end
71
+ def standard_index(relation, represent_with)
72
+ model_klass = relation.base_class
73
+ OSU::AccessPolicy.require_action_allowed!(:index, current_api_user, model_klass)
74
+ respond_with relation, represent_with: represent_with
78
75
  end
79
76
 
80
- def standard_sort(model_klass)
81
- # Take array of all IDs or hash of id => position,
82
- # Regardless, build up an array of all IDs in the right order and pass those to sort
83
-
84
- new_positions = params['newPositions']
85
- return head :no_content if new_positions.length == 0
86
-
87
- # Can't have duplicate positions or IDs
88
- unique_ids = new_positions.collect{|np| np['id']}.uniq
89
- unique_positions = new_positions.collect{|np| np['position']}.uniq
90
-
91
- return head :bad_request if unique_ids.length != new_positions.length
92
- return head :bad_request if unique_positions.length != new_positions.length
93
-
94
- first = model_klass.where(:id => new_positions[0]['id']).first
95
-
96
- return head :not_found if first.blank?
97
-
98
- originalOrdered = first.me_and_peers.ordered.all
99
-
100
- originalOrdered.each do |item|
101
- raise SecurityTransgression unless item.send(:container_column) == originalOrdered[0].send(:container_column) \
102
- if item.respond_to?(:container_column)
103
- OSU::AccessPolicy.require_action_allowed!(:sort, current_api_user, item)
104
- end
105
-
106
- originalOrderedIds = originalOrdered.collect{|sc| sc.id}
107
-
108
- newOrderedIds = Array.new(originalOrderedIds.size)
109
-
110
- new_positions.each do |newPosition|
111
- id = newPosition['id'].to_i
112
- newOrderedIds[newPosition['position']] = id
113
- originalOrderedIds.delete(id)
114
- end
115
-
116
- ptr = 0
117
- for oldId in originalOrderedIds
118
- while !newOrderedIds[ptr].nil?; ptr += 1; end
119
- newOrderedIds[ptr] = oldId
120
- end
121
-
122
- begin
123
- model_klass.sort!(newOrderedIds)
124
- rescue Exception => e
125
- return head :internal_server_error
126
- end
77
+ def standard_search(routine, query, options, represent_with)
78
+ model_klass = routine.send(:initial_relation).base_class
79
+ OSU::AccessPolicy.require_action_allowed!(:search, current_api_user, model_klass)
80
+ outputs = routine.call(query, options).outputs
81
+ respond_with outputs, represent_with: represent_with
82
+ end
127
83
 
128
- head :no_content
84
+ def standard_sort(*args)
85
+ raise NotYetImplemented
129
86
  end
130
87
 
131
88
  end
@@ -1,5 +1,5 @@
1
1
  module OpenStax
2
2
  module Api
3
- VERSION = "2.5.1"
3
+ VERSION = "3.0.0"
4
4
  end
5
5
  end
@@ -1,10 +1,96 @@
1
- require 'spec_helper'
1
+ require 'rails_helper'
2
2
 
3
3
  module OpenStax
4
4
  module Api
5
5
  module V1
6
6
  describe ApiController do
7
- # Nothing to test yet
7
+
8
+ let!(:user) { FactoryGirl.create :user }
9
+ let!(:user_2) { FactoryGirl.create :user }
10
+ let!(:application) { double('Doorkeeper::Application') }
11
+ let!(:doorkeeper_token) { double('Doorkeeper::AccessToken') }
12
+ let!(:non_doorkeeper_user_proc) { lambda { user } }
13
+ let!(:controller) { ApiController.new }
14
+
15
+ context 'no authentication' do
16
+ before (:each) do
17
+ controller.doorkeeper_token = nil
18
+ controller.present_user = nil
19
+ end
20
+
21
+ it 'has no human_user and no application' do
22
+ expect(controller.send :session_user?).to eq false
23
+ expect(controller.current_application).to be_nil
24
+ expect(controller.current_human_user).to be_nil
25
+ expect(controller.current_session_user).to be_nil
26
+ end
27
+ end
28
+
29
+ context 'session' do
30
+ before (:each) do
31
+ controller.doorkeeper_token = nil
32
+ controller.present_user = user
33
+ end
34
+
35
+ it 'has a human_user but no application' do
36
+ expect(controller.send :session_user?).to eq true
37
+ expect(controller.current_application).to be_nil
38
+ expect(controller.current_human_user).to eq user
39
+ expect(controller.current_session_user).to eq user
40
+ end
41
+ end
42
+
43
+ context 'token with application and human user' do
44
+ before (:each) do
45
+ controller.doorkeeper_token = doorkeeper_token
46
+ controller.present_user = nil
47
+ end
48
+
49
+ it 'has a human_user from token and an application' do
50
+ allow(doorkeeper_token).to receive(:application).and_return(application)
51
+ allow(doorkeeper_token).to receive(:resource_owner_id).and_return(user.id)
52
+
53
+ expect(controller.send :session_user?).to eq false
54
+ expect(controller.current_application).to eq application
55
+ expect(controller.current_human_user).to eq user
56
+ expect(controller.current_session_user).to be_nil
57
+ end
58
+ end
59
+
60
+ context 'token with application only' do
61
+ before (:each) do
62
+ controller.doorkeeper_token = doorkeeper_token
63
+ controller.present_user = nil
64
+ end
65
+
66
+ it 'has an application but no human_user' do
67
+ allow(doorkeeper_token).to receive(:application).and_return(application)
68
+ allow(doorkeeper_token).to receive(:resource_owner_id).and_return(nil)
69
+
70
+ expect(controller.send :session_user?).to eq false
71
+ expect(controller.current_application).to eq application
72
+ expect(controller.current_human_user).to eq nil
73
+ expect(controller.current_session_user).to eq nil
74
+ end
75
+ end
76
+
77
+ context 'session and token' do
78
+ before (:each) do
79
+ controller.doorkeeper_token = doorkeeper_token
80
+ controller.present_user = user_2
81
+ end
82
+
83
+ it 'ignores the session unless explicitly asked' do
84
+ allow(doorkeeper_token).to receive(:application).and_return(application)
85
+ allow(doorkeeper_token).to receive(:resource_owner_id).and_return(user)
86
+
87
+ expect(controller.send :session_user?).to eq false
88
+ expect(controller.current_application).to eq application
89
+ expect(controller.current_human_user).to eq user
90
+ expect(controller.current_session_user).to eq user_2
91
+ end
92
+ end
93
+
8
94
  end
9
95
  end
10
96
  end
data/spec/dummy/README.md CHANGED
@@ -1 +1 @@
1
- Dummy application used to test the openstax_api gem.
1
+ Dummy application used to test the openstax_api gem.
data/spec/dummy/Rakefile CHANGED
@@ -1,7 +1,6 @@
1
- #!/usr/bin/env rake
2
1
  # Add your own tasks in files placed in lib/tasks ending in .rake,
3
2
  # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
4
3
 
5
4
  require File.expand_path('../config/application', __FILE__)
6
5
 
7
- Dummy::Application.load_tasks
6
+ Rails.application.load_tasks
@@ -5,11 +5,9 @@
5
5
  // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
6
  //
7
7
  // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
- // the compiled file.
8
+ // compiled file.
9
9
  //
10
- // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
- // GO AFTER THE REQUIRES BELOW.
10
+ // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
12
  //
13
- //= require jquery
14
- //= require jquery_ujs
15
13
  //= require_tree .
@@ -5,9 +5,11 @@
5
5
  * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
6
  * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
7
  *
8
- * You're free to add application-wide styles to this file and they'll appear at the top of the
9
- * compiled file, but it's generally better to create a new file per style scope.
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
10
12
  *
11
- *= require_self
12
13
  *= require_tree .
14
+ *= require_self
13
15
  */
@@ -0,0 +1,2 @@
1
+ class User < ActiveRecord::Base
2
+ end
@@ -0,0 +1,13 @@
1
+ require 'representable/json'
2
+
3
+ class UserRepresenter < ::Roar::Decorator
4
+
5
+ include Roar::Representer::JSON
6
+
7
+ property :username, readable: false, writeable: false,
8
+ schema_info: { required: true }
9
+ property :name, readable: true, writeable: true
10
+ property :email, readable: false, writeable: true
11
+ property :password_hash, readable: false, writeable: false
12
+
13
+ end
@@ -0,0 +1,5 @@
1
+ require 'representable/json'
2
+
3
+ class UserSearchRepresenter < OpenStax::Api::V1::AbstractSearchRepresenter
4
+ collection :items, inherit: true, class: User, decorator: UserRepresenter
5
+ end