openstax_api 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/openstax/api/v1/api_controller.rb +9 -145
- data/app/controllers/openstax/api/v1/oauth_based_api_controller.rb +0 -2
- data/app/models/openstax/api/api_user.rb +5 -4
- data/lib/openstax/api/apipie.rb +51 -0
- data/lib/{openstax_api → openstax/api}/constraints.rb +0 -0
- data/lib/{openstax_api → openstax/api}/doorkeeper_extensions.rb +0 -0
- data/lib/{openstax_api → openstax/api}/engine.rb +0 -0
- data/lib/openstax/api/representable_schema_printer.rb +83 -0
- data/lib/openstax/api/roar.rb +132 -0
- data/lib/{openstax_api → openstax/api}/route_extensions.rb +1 -1
- data/lib/{openstax_api → openstax/api}/version.rb +1 -1
- data/lib/openstax_api.rb +20 -3
- data/spec/app/models/openstax/api/api_user_spec.rb +1 -1
- data/spec/app/representers/openstax/api/v1/representable_schema_printer_spec.rb +1 -1
- data/spec/dummy/app/models/dummy_user.rb +2 -0
- data/spec/dummy/app/representers/{user_representer.rb → dummy_user_representer.rb} +1 -1
- data/spec/dummy/config/initializers/openstax_api.rb +3 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/{1_create_users.rb → 1_create_dummy_users.rb} +2 -2
- data/spec/dummy/db/schema.rb +7 -7
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +101 -597
- data/spec/dummy/log/test.log +62 -3216
- data/spec/lib/openstax/api/apipie.rb +11 -0
- data/spec/lib/{openstax_api → openstax/api}/constraints_spec.rb +0 -0
- data/spec/lib/{openstax_api → openstax/api}/doorkeeper_extensions_spec.rb +0 -0
- data/spec/lib/openstax/api/roar.rb +11 -0
- data/spec/lib/{openstax_api → openstax/api}/route_extensions_spec.rb +0 -0
- metadata +42 -20
- data/app/representers/openstax/api/v1/representable_schema_printer.rb +0 -85
- data/spec/dummy/app/models/user.rb +0 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0182a7fe7cdaf9adbe7a8719473cd4546c2f3bcb
|
4
|
+
data.tar.gz: be4936d879e58990235b59857c65892fc1711456
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2948208fe965b6e01bc5e67615b48058d52998fb40e4f2db620e4ee646f6559b19c0725439956f2107dc171b698b718b9252703a267a484ff324dcb7208949aa
|
7
|
+
data.tar.gz: 433bd360c6317cde8864ad9933877605387eaf3a8d92ee3267836abdce5e143663b2cb65acdec7f7ce735b0b4adb266ab5f97e04020c14496f9c7daaed6b9b3a
|
@@ -1,4 +1,7 @@
|
|
1
1
|
require 'roar-rails'
|
2
|
+
require 'exception_notification'
|
3
|
+
require 'openstax/api/roar'
|
4
|
+
require 'openstax/api/apipie'
|
2
5
|
|
3
6
|
module OpenStax
|
4
7
|
module Api
|
@@ -6,7 +9,9 @@ module OpenStax
|
|
6
9
|
|
7
10
|
class ApiController < ::ApplicationController
|
8
11
|
|
9
|
-
include Roar::Rails::ControllerAdditions
|
12
|
+
include ::Roar::Rails::ControllerAdditions
|
13
|
+
include OpenStax::Api::Roar
|
14
|
+
include OpenStax::Api::Apipie
|
10
15
|
|
11
16
|
fine_print_skip_signatures(:general_terms_of_use,
|
12
17
|
:privacy_policy) \
|
@@ -19,18 +24,7 @@ module OpenStax
|
|
19
24
|
respond_to :json
|
20
25
|
rescue_from Exception, :with => :rescue_from_exception
|
21
26
|
|
22
|
-
|
23
|
-
return if Rails.env.test?
|
24
|
-
raise IllegalArgument, "must supply a :url parameter" if !options[:url_base]
|
25
|
-
|
26
|
-
url_base = options[:url_base].is_a?(Symbol) ?
|
27
|
-
UrlGenerator.new.send(options[:url_base], protocol: 'https') :
|
28
|
-
options[:url_base].to_s
|
29
|
-
|
30
|
-
"#{url_base}/#{options[:url_end] || ''}"
|
31
|
-
end
|
32
|
-
|
33
|
-
# TODO doorkeeper users (or rather users who have doorkeeper
|
27
|
+
# TODO: doorkeeper users (or rather users who have doorkeeper
|
34
28
|
# applications) need to agree to API terms of use (need to have agreed
|
35
29
|
# to it at one time, can't require them to agree when terms change since
|
36
30
|
# their apps are doing the talking) -- this needs more thought
|
@@ -39,13 +33,11 @@ module OpenStax
|
|
39
33
|
@current_user ||= doorkeeper_token ?
|
40
34
|
User.find(doorkeeper_token.resource_owner_id) :
|
41
35
|
super
|
42
|
-
# TODO maybe freak out if current user is anonymous (require we know
|
36
|
+
# TODO: maybe freak out if current user is anonymous (require we know
|
43
37
|
# who person/app is so we can do things like throttling, API terms
|
44
38
|
# agreement, etc)
|
45
39
|
end
|
46
40
|
|
47
|
-
|
48
|
-
|
49
41
|
protected
|
50
42
|
|
51
43
|
def rescue_from_exception(exception)
|
@@ -66,147 +58,19 @@ module OpenStax
|
|
66
58
|
end
|
67
59
|
|
68
60
|
if notify
|
69
|
-
# TODO: Not yet in OSU
|
70
|
-
=begin
|
71
61
|
ExceptionNotifier.notify_exception(
|
72
62
|
exception,
|
73
63
|
env: request.env,
|
74
64
|
data: { message: "An exception occurred" }
|
75
65
|
)
|
76
|
-
|
66
|
+
|
77
67
|
Rails.logger.error("An exception occurred: #{exception.message}\n\n#{exception.backtrace.join("\n")}") \
|
78
68
|
end
|
79
69
|
|
80
70
|
head error
|
81
71
|
end
|
82
72
|
|
83
|
-
def self.json_schema(representer, options={})
|
84
|
-
RepresentableSchemaPrinter.json(representer, options)
|
85
|
-
end
|
86
|
-
|
87
|
-
# A hack at a conversion from a Representer to a series of Apipie declarations
|
88
|
-
# Can call it like any Apipie DSL method,
|
89
|
-
#
|
90
|
-
# example "blah"
|
91
|
-
# representer Api::V1::ExerciseRepresenter
|
92
|
-
# def update ...
|
93
|
-
#
|
94
|
-
def self.representer(representer)
|
95
|
-
representer.representable_attrs.each do |attr|
|
96
|
-
schema_info = attr.options[:schema_info] || {}
|
97
|
-
param attr.name, (attr.options[:type] || Object), required: schema_info[:required]
|
98
|
-
end
|
99
|
-
end
|
100
73
|
|
101
|
-
def get_representer(represent_with, model=nil)
|
102
|
-
return nil if represent_with.nil?
|
103
|
-
if represent_with.is_a? Proc
|
104
|
-
represent_with.call(model)
|
105
|
-
else
|
106
|
-
represent_with
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def rest_get(model_klass, id, represent_with=nil)
|
111
|
-
@model = model_klass.find(id)
|
112
|
-
raise SecurityTransgression unless current_user.can_read?(@model)
|
113
|
-
respond_with @model, represent_with: get_representer(represent_with, @model)
|
114
|
-
end
|
115
|
-
|
116
|
-
def rest_update(model_klass, id, represent_with=nil)
|
117
|
-
@model = model_klass.find(id)
|
118
|
-
raise SecurityTransgression unless current_user.can_update?(@model)
|
119
|
-
consume!(@model, represent_with: get_representer(represent_with, @model))
|
120
|
-
|
121
|
-
if @model.save
|
122
|
-
head :no_content
|
123
|
-
else
|
124
|
-
render json: @model.errors, status: :unprocessable_entity
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def rest_create(model_klass)
|
129
|
-
@model = model_klass.new()
|
130
|
-
|
131
|
-
# Unlike the implications of the representable README, "consume!" can
|
132
|
-
# actually make changes to the database. See http://goo.gl/WVLBqA.
|
133
|
-
# We do want to consume before checking the permissions so we can know
|
134
|
-
# what we're dealing with, but if user doesn't have permission we don't
|
135
|
-
# want to have changed the DB. Wrap in a transaction to protect ourselves.
|
136
|
-
|
137
|
-
model_klass.transaction do
|
138
|
-
consume!(@model)
|
139
|
-
raise SecurityTransgression unless current_user.can_create?(@model)
|
140
|
-
end
|
141
|
-
|
142
|
-
if @model.save
|
143
|
-
respond_with @model
|
144
|
-
else
|
145
|
-
render json: @model.errors, status: :unprocessable_entity
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
def rest_destroy(model_klass, id)
|
150
|
-
@model = model_klass.find(id)
|
151
|
-
raise SecurityTransgression unless current_user.can_destroy?(@model)
|
152
|
-
|
153
|
-
if @model.destroy
|
154
|
-
head :no_content
|
155
|
-
else
|
156
|
-
render json: @model.errors, status: :unprocessable_entity
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
def standard_sort(model_klass)
|
161
|
-
# take array of all IDs or hash of id => position,
|
162
|
-
# regardless build up an array of all IDs in the right order and pass those to sort
|
163
|
-
|
164
|
-
new_positions = params['newPositions']
|
165
|
-
return head :no_content if new_positions.length == 0
|
166
|
-
|
167
|
-
# Can't have duplicate positions or IDs
|
168
|
-
unique_ids = new_positions.collect{|np| np['id']}.uniq
|
169
|
-
unique_positions = new_positions.collect{|np| np['position']}.uniq
|
170
|
-
|
171
|
-
return head :bad_request if unique_ids.length != new_positions.length
|
172
|
-
return head :bad_request if unique_positions.length != new_positions.length
|
173
|
-
|
174
|
-
first = model_klass.where(:id => new_positions[0]['id']).first
|
175
|
-
|
176
|
-
return head :not_found if first.blank?
|
177
|
-
|
178
|
-
originalOrdered = first.me_and_peers.ordered.all
|
179
|
-
|
180
|
-
originalOrdered.each do |item|
|
181
|
-
raise SecurityTransgression unless item.send(:container_column) == originalOrdered[0].send(:container_column) \
|
182
|
-
if item.respond_to?(:container_column)
|
183
|
-
raise SecurityTransgression unless current_user.can_sort?(item)
|
184
|
-
end
|
185
|
-
|
186
|
-
originalOrderedIds = originalOrdered.collect{|sc| sc.id}
|
187
|
-
|
188
|
-
newOrderedIds = Array.new(originalOrderedIds.size)
|
189
|
-
|
190
|
-
new_positions.each do |newPosition|
|
191
|
-
id = newPosition['id'].to_i
|
192
|
-
newOrderedIds[newPosition['position']] = id
|
193
|
-
originalOrderedIds.delete(id)
|
194
|
-
end
|
195
|
-
|
196
|
-
ptr = 0
|
197
|
-
for oldId in originalOrderedIds
|
198
|
-
while !newOrderedIds[ptr].nil?; ptr += 1; end
|
199
|
-
newOrderedIds[ptr] = oldId
|
200
|
-
end
|
201
|
-
|
202
|
-
begin
|
203
|
-
model_klass.sort!(newOrderedIds)
|
204
|
-
rescue Exception => e
|
205
|
-
return head :internal_server_error
|
206
|
-
end
|
207
|
-
|
208
|
-
head :no_content
|
209
|
-
end
|
210
74
|
|
211
75
|
end
|
212
76
|
|
@@ -11,6 +11,8 @@
|
|
11
11
|
# This API class gives us a way to abstract out these cases and also
|
12
12
|
# gives us accessors to get the Application and User objects, if available.
|
13
13
|
|
14
|
+
require 'openstax_utilities'
|
15
|
+
|
14
16
|
module OpenStax
|
15
17
|
module Api
|
16
18
|
class ApiUser
|
@@ -25,10 +27,11 @@ module OpenStax
|
|
25
27
|
# procs that can get it for us. This could save us some queries.
|
26
28
|
|
27
29
|
if doorkeeper_token
|
30
|
+
user_class = OpenStax::Api.configuration.user_class_name.classify.constantize
|
28
31
|
@application_proc = lambda { doorkeeper_token.application }
|
29
32
|
@user_proc = lambda {
|
30
33
|
doorkeeper_token.resource_owner_id ?
|
31
|
-
|
34
|
+
user_class.find(doorkeeper_token.resource_owner_id) :
|
32
35
|
nil
|
33
36
|
}
|
34
37
|
else
|
@@ -37,9 +40,7 @@ module OpenStax
|
|
37
40
|
end
|
38
41
|
end
|
39
42
|
|
40
|
-
# Returns a Doorkeeper::Application or nil
|
41
|
-
# TODO should we have a NoApplication like NoUser (or maybe should
|
42
|
-
# NoUser just be replaced with nil)
|
43
|
+
# Returns a Doorkeeper::Application or nil
|
43
44
|
def application
|
44
45
|
@application ||= @application_proc.call
|
45
46
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Copyright 2011-2014 Rice University. Licensed under the Affero General Public
|
2
|
+
# License version 3 or later. See the COPYRIGHT file for details.
|
3
|
+
|
4
|
+
require 'openstax/api/representable_schema_printer'
|
5
|
+
|
6
|
+
module OpenStax
|
7
|
+
module Api
|
8
|
+
|
9
|
+
module Apipie
|
10
|
+
|
11
|
+
def self.included(base)
|
12
|
+
base.send :extend, ClassMethods
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
|
17
|
+
def api_example(options={})
|
18
|
+
return if Rails.env.test?
|
19
|
+
raise IllegalArgument, "must supply a :url parameter" if !options[:url_base]
|
20
|
+
|
21
|
+
url_base = options[:url_base].is_a?(Symbol) ?
|
22
|
+
UrlGenerator.new.send(options[:url_base], protocol: 'https') :
|
23
|
+
options[:url_base].to_s
|
24
|
+
|
25
|
+
"#{url_base}/#{options[:url_end] || ''}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def json_schema(representer, options={})
|
29
|
+
RepresentableSchemaPrinter.json(representer, options)
|
30
|
+
end
|
31
|
+
|
32
|
+
# A hack at a conversion from a Representer to a series of Apipie declarations
|
33
|
+
# Can call it like any Apipie DSL method,
|
34
|
+
#
|
35
|
+
# example "blah"
|
36
|
+
# representer Api::V1::ExerciseRepresenter
|
37
|
+
# def update ...
|
38
|
+
#
|
39
|
+
def representer(representer)
|
40
|
+
representer.representable_attrs.each do |attr|
|
41
|
+
schema_info = attr.options[:schema_info] || {}
|
42
|
+
param attr.name, (attr.options[:type] || Object), required: schema_info[:required]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module OpenStax
|
2
|
+
module Api
|
3
|
+
class RepresentableSchemaPrinter
|
4
|
+
|
5
|
+
def self.json(representer, options={})
|
6
|
+
options[:include] ||= [:readable, :writeable]
|
7
|
+
options[:indent] ||= ' '
|
8
|
+
|
9
|
+
definitions = {}
|
10
|
+
|
11
|
+
schema = json_schema(representer, definitions, options)
|
12
|
+
schema[:definitions] = definitions
|
13
|
+
|
14
|
+
json_string = JSON.pretty_generate(schema, {indent: options[:indent]})
|
15
|
+
|
16
|
+
"\nSchema {##{SecureRandom.hex(4)} .schema}\n------\n" +
|
17
|
+
"<pre class='code'>\n#{json_string}\n</pre>\n"
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def self.json_schema(representer, definitions, options={})
|
23
|
+
schema = {
|
24
|
+
# id: schema_id(representer),
|
25
|
+
# title: schema_title(representer),
|
26
|
+
type: "object",
|
27
|
+
properties: {},
|
28
|
+
required: []
|
29
|
+
# :$schema => "http://json-schema.org/draft-04/schema#"
|
30
|
+
}
|
31
|
+
|
32
|
+
representer.representable_attrs.each do |attr|
|
33
|
+
schema_info = attr.options[:schema_info] || {}
|
34
|
+
|
35
|
+
schema[:required].push(attr.name) if schema_info[:required]
|
36
|
+
|
37
|
+
next unless [options[:include]].flatten.any?{|inc| attr.send(inc.to_s+"?") || schema_info[:required]}
|
38
|
+
|
39
|
+
attr_info = {}
|
40
|
+
|
41
|
+
if attr.options[:collection]
|
42
|
+
attr_info[:type] = "array"
|
43
|
+
else
|
44
|
+
attr_info[:type] = attr.options[:type].to_s.downcase if attr.options[:type]
|
45
|
+
end
|
46
|
+
|
47
|
+
schema_info.each do |key, value|
|
48
|
+
next if [:required].include?(key)
|
49
|
+
value = value.to_s.downcase if key == :type
|
50
|
+
attr_info[key] = value
|
51
|
+
end
|
52
|
+
|
53
|
+
decorator = attr.options[:decorator].try(:is_a?, Proc) ? nil : attr.options[:decorator]
|
54
|
+
|
55
|
+
if decorator
|
56
|
+
relative_schema_id(decorator).tap do |id|
|
57
|
+
attr_info[:$ref] = "#/definitions/#{id}"
|
58
|
+
definitions[id] ||= json_schema(decorator, definitions, options)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
schema[:properties][attr.name.to_sym] = attr_info
|
63
|
+
end
|
64
|
+
|
65
|
+
schema
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.schema_title(representer)
|
69
|
+
representer.name.gsub(/Representer/,'')
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.schema_id(representer)
|
73
|
+
"http://#{OpenStax::Api::Engine::MAIN_APP_NAME.to_s}.openstax.org/" +
|
74
|
+
"#{schema_title(representer).downcase.gsub(/::/,'/')}"
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.relative_schema_id(representer)
|
78
|
+
representer.name.gsub(/Representer/,'').downcase.gsub(/::/,'/')
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# Copyright 2011-2014 Rice University. Licensed under the Affero General Public
|
2
|
+
# License version 3 or later. See the COPYRIGHT file for details.
|
3
|
+
|
4
|
+
module OpenStax
|
5
|
+
module Api
|
6
|
+
|
7
|
+
module Roar
|
8
|
+
|
9
|
+
def get_representer(represent_with, model=nil)
|
10
|
+
return nil if represent_with.nil?
|
11
|
+
if represent_with.is_a? Proc
|
12
|
+
represent_with.call(model)
|
13
|
+
else
|
14
|
+
represent_with
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def standard_read(model_klass, id, represent_with=nil)
|
19
|
+
@model = model_klass.find(id)
|
20
|
+
raise SecurityTransgression unless current_user.can_read?(@model)
|
21
|
+
respond_with @model, represent_with: get_representer(represent_with, @model)
|
22
|
+
end
|
23
|
+
|
24
|
+
def standard_update(model_klass, id, represent_with=nil)
|
25
|
+
@model = model_klass.find(id)
|
26
|
+
raise SecurityTransgression unless current_user.can_update?(@model)
|
27
|
+
consume!(@model, represent_with: get_representer(represent_with, @model))
|
28
|
+
|
29
|
+
if @model.save
|
30
|
+
head :no_content
|
31
|
+
else
|
32
|
+
render json: @model.errors, status: :unprocessable_entity
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def standard_create(model_klass, represent_with=nil)
|
37
|
+
standard_nested_create(model_klass, nil, nil, represent_with)
|
38
|
+
end
|
39
|
+
|
40
|
+
def standard_nested_create(model_klass, container_association=nil, container_id=nil, represent_with=nil)
|
41
|
+
@model = model_klass.new()
|
42
|
+
|
43
|
+
if container_association && container_id
|
44
|
+
foreign_key = model_klass.reflect_on_association(container_association).association_foreign_key
|
45
|
+
@model.send(foreign_key + '=', container_id)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Unlike the implications of the representable README, "consume!" can
|
49
|
+
# actually make changes to the database. See http://goo.gl/WVLBqA.
|
50
|
+
# We do want to consume before checking the permissions so we can know
|
51
|
+
# what we're dealing with, but if user doesn't have permission we don't
|
52
|
+
# want to have changed the DB. Wrap in a transaction to protect ourselves.
|
53
|
+
|
54
|
+
model_klass.transaction do
|
55
|
+
consume!(@model, represent_with: get_representer(represent_with, @model))
|
56
|
+
yield @model if block_given?
|
57
|
+
raise SecurityTransgression unless current_user.can_create?(@model)
|
58
|
+
end
|
59
|
+
|
60
|
+
if @model.save
|
61
|
+
respond_with @model, represent_with: get_representer(represent_with, @model), status: :created
|
62
|
+
else
|
63
|
+
render json: @model.errors, status: :unprocessable_entity
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def standard_destroy(model_klass, id)
|
68
|
+
@model = model_klass.find(id)
|
69
|
+
raise SecurityTransgression unless current_user.can_destroy?(@model)
|
70
|
+
|
71
|
+
if @model.destroy
|
72
|
+
head :no_content
|
73
|
+
else
|
74
|
+
render json: @model.errors, status: :unprocessable_entity
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def standard_sort(model_klass)
|
79
|
+
# Take array of all IDs or hash of id => position,
|
80
|
+
# Regardless, build up an array of all IDs in the right order and pass those to sort
|
81
|
+
|
82
|
+
new_positions = params['newPositions']
|
83
|
+
return head :no_content if new_positions.length == 0
|
84
|
+
|
85
|
+
# Can't have duplicate positions or IDs
|
86
|
+
unique_ids = new_positions.collect{|np| np['id']}.uniq
|
87
|
+
unique_positions = new_positions.collect{|np| np['position']}.uniq
|
88
|
+
|
89
|
+
return head :bad_request if unique_ids.length != new_positions.length
|
90
|
+
return head :bad_request if unique_positions.length != new_positions.length
|
91
|
+
|
92
|
+
first = model_klass.where(:id => new_positions[0]['id']).first
|
93
|
+
|
94
|
+
return head :not_found if first.blank?
|
95
|
+
|
96
|
+
originalOrdered = first.me_and_peers.ordered.all
|
97
|
+
|
98
|
+
originalOrdered.each do |item|
|
99
|
+
raise SecurityTransgression unless item.send(:container_column) == originalOrdered[0].send(:container_column) \
|
100
|
+
if item.respond_to?(:container_column)
|
101
|
+
raise SecurityTransgression unless current_user.can_sort?(item)
|
102
|
+
end
|
103
|
+
|
104
|
+
originalOrderedIds = originalOrdered.collect{|sc| sc.id}
|
105
|
+
|
106
|
+
newOrderedIds = Array.new(originalOrderedIds.size)
|
107
|
+
|
108
|
+
new_positions.each do |newPosition|
|
109
|
+
id = newPosition['id'].to_i
|
110
|
+
newOrderedIds[newPosition['position']] = id
|
111
|
+
originalOrderedIds.delete(id)
|
112
|
+
end
|
113
|
+
|
114
|
+
ptr = 0
|
115
|
+
for oldId in originalOrderedIds
|
116
|
+
while !newOrderedIds[ptr].nil?; ptr += 1; end
|
117
|
+
newOrderedIds[ptr] = oldId
|
118
|
+
end
|
119
|
+
|
120
|
+
begin
|
121
|
+
model_klass.sort!(newOrderedIds)
|
122
|
+
rescue Exception => e
|
123
|
+
return head :internal_server_error
|
124
|
+
end
|
125
|
+
|
126
|
+
head :no_content
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
data/lib/openstax_api.rb
CHANGED
@@ -1,8 +1,25 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require '
|
1
|
+
require 'openstax/api/engine'
|
2
|
+
require 'openstax/api/doorkeeper_extensions'
|
3
|
+
require 'openstax/api/route_extensions'
|
4
4
|
|
5
5
|
module OpenStax
|
6
6
|
module Api
|
7
|
+
|
8
|
+
def self.configure
|
9
|
+
yield configuration
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.configuration
|
13
|
+
@configuration ||= Configuration.new
|
14
|
+
end
|
15
|
+
|
16
|
+
class Configuration
|
17
|
+
attr_accessor :user_class_name
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@user_class_name = 'User'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
7
24
|
end
|
8
25
|
end
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
module OpenStax
|
4
4
|
module Api
|
5
5
|
describe ApiUser do
|
6
|
-
let(:user) {
|
6
|
+
let(:user) { DummyUser.create }
|
7
7
|
let(:application) { double('Doorkeeper::Application') }
|
8
8
|
let(:doorkeeper_token) { double('Doorkeeper::AccessToken') }
|
9
9
|
let(:non_doorkeeper_user_proc) { lambda { user } }
|
@@ -5,7 +5,7 @@ module OpenStax
|
|
5
5
|
module V1
|
6
6
|
describe RepresentableSchemaPrinter do
|
7
7
|
it 'must print model schemas' do
|
8
|
-
schema = RepresentableSchemaPrinter.json(
|
8
|
+
schema = RepresentableSchemaPrinter.json(DummyUserRepresenter)
|
9
9
|
expect(schema).to include('Schema')
|
10
10
|
expect(schema).to include('.schema')
|
11
11
|
expect(schema).to include('------')
|
Binary file
|
data/spec/dummy/db/schema.rb
CHANGED
@@ -13,6 +13,13 @@
|
|
13
13
|
|
14
14
|
ActiveRecord::Schema.define(:version => 1) do
|
15
15
|
|
16
|
+
create_table "dummy_users", :force => true do |t|
|
17
|
+
t.string "username"
|
18
|
+
t.string "password_hash"
|
19
|
+
t.datetime "created_at", :null => false
|
20
|
+
t.datetime "updated_at", :null => false
|
21
|
+
end
|
22
|
+
|
16
23
|
create_table "oauth_access_grants", :force => true do |t|
|
17
24
|
t.integer "resource_owner_id", :null => false
|
18
25
|
t.integer "application_id", :null => false
|
@@ -52,11 +59,4 @@ ActiveRecord::Schema.define(:version => 1) do
|
|
52
59
|
|
53
60
|
add_index "oauth_applications", ["uid"], :name => "index_oauth_applications_on_uid", :unique => true
|
54
61
|
|
55
|
-
create_table "users", :force => true do |t|
|
56
|
-
t.string "username"
|
57
|
-
t.string "password_hash"
|
58
|
-
t.datetime "created_at", :null => false
|
59
|
-
t.datetime "updated_at", :null => false
|
60
|
-
end
|
61
|
-
|
62
62
|
end
|
data/spec/dummy/db/test.sqlite3
CHANGED
Binary file
|