openstax_api 2.5.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +4 -6
- data/app/controllers/openstax/api/v1/api_controller.rb +1 -1
- data/app/representers/openstax/api/v1/abstract_search_representer.rb +40 -0
- data/lib/openstax/api/constraints.rb +3 -4
- data/lib/openstax/api/engine.rb +3 -8
- data/lib/openstax/api/representable_schema_printer.rb +58 -35
- data/lib/openstax/api/roar.rb +50 -93
- data/lib/openstax/api/version.rb +1 -1
- data/spec/controllers/openstax/api/v1/api_controller_spec.rb +88 -2
- data/spec/dummy/README.md +1 -1
- data/spec/dummy/Rakefile +1 -2
- data/spec/dummy/app/assets/javascripts/application.js +3 -5
- data/spec/dummy/app/assets/stylesheets/application.css +5 -3
- data/spec/dummy/app/models/user.rb +2 -0
- data/spec/dummy/app/representers/user_representer.rb +13 -0
- data/spec/dummy/app/representers/user_search_representer.rb +5 -0
- data/spec/dummy/app/routines/search_users.rb +22 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config/application.rb +0 -30
- data/spec/dummy/config/boot.rb +4 -9
- data/spec/dummy/config/database.yml +8 -8
- data/spec/dummy/config/environment.rb +3 -3
- data/spec/dummy/config/environments/development.rb +21 -13
- data/spec/dummy/config/environments/production.rb +41 -32
- data/spec/dummy/config/environments/test.rb +17 -13
- data/spec/dummy/config/initializers/assets.rb +8 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +6 -5
- data/spec/dummy/config/initializers/mime_types.rb +0 -1
- data/spec/dummy/config/initializers/openstax_api.rb +2 -2
- data/spec/dummy/config/initializers/session_store.rb +1 -6
- data/spec/dummy/config/initializers/wrap_parameters.rb +6 -6
- data/spec/dummy/config/locales/en.yml +20 -2
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config.ru +1 -1
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/1_create_users.rb +16 -0
- data/spec/dummy/db/schema.rb +38 -32
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/lib/controller_includes.rb +2 -6
- data/spec/dummy/log/development.log +486 -1445
- data/spec/dummy/log/test.log +16720 -3757
- data/spec/dummy/public/404.html +54 -13
- data/spec/dummy/public/422.html +54 -13
- data/spec/dummy/public/500.html +53 -12
- data/spec/factories/user.rb +8 -0
- data/spec/lib/openstax/api/apipie_spec.rb +15 -0
- data/spec/lib/openstax/api/constraints_spec.rb +6 -6
- data/spec/lib/openstax/api/doorkeeper_application_includes_spec.rb +1 -1
- data/spec/lib/openstax/api/representable_schema_printer_spec.rb +5 -3
- data/spec/lib/openstax/api/roar_spec.rb +22 -0
- data/spec/lib/openstax/api/routing_mapper_includes_spec.rb +1 -1
- data/spec/lib/openstax_api_spec.rb +19 -0
- data/spec/models/openstax/api/api_user_spec.rb +2 -2
- data/spec/rails_helper.rb +54 -0
- data/spec/representers/openstax/api/v1/abstract_search_representer_spec.rb +127 -0
- data/spec/spec_helper.rb +80 -13
- metadata +85 -18
- data/spec/dummy/app/models/dummy_user.rb +0 -2
- data/spec/dummy/app/representers/dummy_user_representer.rb +0 -9
- data/spec/dummy/config/initializers/secret_token.rb +0 -7
- data/spec/dummy/db/migrate/1_create_dummy_users.rb +0 -10
- data/spec/dummy/script/rails +0 -6
- data/spec/lib/openstax/api/apipie.rb +0 -11
- data/spec/lib/openstax/api/roar.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4a4d7a6e7dd7a7ccda5ca535edb33ce7dc6694c
|
4
|
+
data.tar.gz: 1337c07dcb6b7097a7411850b09781ab0729110e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 '
|
14
|
+
require 'rspec/core'
|
15
|
+
require 'rspec/core/rake_task'
|
15
16
|
|
16
|
-
|
17
|
-
|
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
|
@@ -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
|
-
|
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)
|
data/lib/openstax/api/engine.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
#
|
72
|
-
#
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
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
|
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
|
|
data/lib/openstax/api/roar.rb
CHANGED
@@ -8,43 +8,46 @@ module OpenStax
|
|
8
8
|
|
9
9
|
module Roar
|
10
10
|
|
11
|
-
def
|
12
|
-
|
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(
|
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
|
17
|
+
respond_with model, represent_with: represent_with
|
24
18
|
end
|
25
19
|
|
26
|
-
def standard_update(
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
39
|
-
|
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(
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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!(
|
58
|
-
yield
|
59
|
-
OSU::AccessPolicy.require_action_allowed!(:create, current_api_user,
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
70
|
-
|
71
|
-
OSU::AccessPolicy.require_action_allowed!(:
|
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
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
-
|
84
|
+
def standard_sort(*args)
|
85
|
+
raise NotYetImplemented
|
129
86
|
end
|
130
87
|
|
131
88
|
end
|
data/lib/openstax/api/version.rb
CHANGED
@@ -1,10 +1,96 @@
|
|
1
|
-
require '
|
1
|
+
require 'rails_helper'
|
2
2
|
|
3
3
|
module OpenStax
|
4
4
|
module Api
|
5
5
|
module V1
|
6
6
|
describe ApiController do
|
7
|
-
|
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
|
-
|
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
|
-
//
|
8
|
+
// compiled file.
|
9
9
|
//
|
10
|
-
//
|
11
|
-
//
|
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
|
9
|
-
* compiled file
|
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,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
|