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.
- 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
|