openstax_api 3.0.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0a201392708c1dc1aea7657701f4b652d572b527
4
- data.tar.gz: 490312d997713a3fde94c3490a0363d99db95cfc
3
+ metadata.gz: a3b74dc93f1361a2e644ad8f4977ae5daa9d27c0
4
+ data.tar.gz: 2258ff732a6fc77dc869a7826812f2dcbdadb9f9
5
5
  SHA512:
6
- metadata.gz: a62157873835a92e372a2af20ec6ab5a6a957d346c411f08ca4ad1e871d06acaeb7b21837c3b3bff4cbc5c32deacff88eb993d6260ff072ed97df0ffcfdd4478
7
- data.tar.gz: 6c1fe337a3919a2f2d16a2430ad7bac65183a926f10169a16547bb83f5669721416beb3498cddf413e5dbc8b702c55871a92700e0ec598ce69ec24bc3ad17c6b
6
+ metadata.gz: 05e1dd62fe6cede4ce4778ca1ad107210d3337c726b6f669f38f36b48b10c23e3472e90d88920793b2844fa6cc3f32b836ca058ea1badb000ab758aead0fbc5b
7
+ data.tar.gz: 9815ae41155f318cfdf003d8e8daaa3d0bae2c0eaf545beaa5308fbef1dab459b88e1dcb76774e0c2430d5db5f73b7bcbc557cb5bfc96390e087896eed38c12e
@@ -31,7 +31,15 @@ module OpenStax
31
31
  }
32
32
 
33
33
  def total_count
34
- represented[:total_count] || represented[:items].limit(nil).offset(nil).count
34
+ return represented[:total_count] if represented[:total_count]
35
+ case represented[:items]
36
+ when ActiveRecord::Relation
37
+ represented[:items].limit(nil).offset(nil).count
38
+ when Array
39
+ represented[:items].count
40
+ else
41
+ 1
42
+ end
35
43
  end
36
44
 
37
45
  end
@@ -13,6 +13,10 @@ module OpenStax
13
13
  def is_admin?
14
14
  false
15
15
  end
16
+
17
+ def is_anonymous?
18
+ false
19
+ end
16
20
  end
17
21
  end
18
22
  end
@@ -1,3 +1,4 @@
1
+ require 'doorkeeper'
1
2
  require 'roar-rails'
2
3
  require 'exception_notification'
3
4
  require 'openstax_utilities'
@@ -27,7 +27,7 @@ module OpenStax
27
27
 
28
28
  # Attempts to extract the given representer's name
29
29
  def self.representer_name(representer)
30
- name = representer.name
30
+ name = representer.try :name
31
31
  return nil if name.nil?
32
32
  name.chomp('Representer').demodulize.camelize(:lower)
33
33
  end
@@ -85,10 +85,13 @@ module OpenStax
85
85
 
86
86
  # Get the nested representers
87
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
88
+ # If we have no instance or class (since we have no fragment), we pass Object
89
+ # By convention, our callables should return an array of all possible
90
+ # representers when we pass the :all_sub_representers => true option
91
+ instance = attr[:instance].evaluate(representer, {}) rescue nil
92
+ klass = attr[:class].evaluate(representer, {}) rescue Object
93
+ decorators = [attr[:extend].evaluate(representer, instance || klass,
94
+ :all_sub_representers => true)].flatten.compact rescue []
92
95
 
93
96
  # Count the representers
94
97
  include_oneof = decorators.length > 1
@@ -2,21 +2,43 @@
2
2
  # License version 3 or later. See the COPYRIGHT file for details.
3
3
 
4
4
  require 'openstax_utilities'
5
+ require 'lev'
5
6
 
6
7
  module OpenStax
7
8
  module Api
8
9
 
9
10
  module Roar
10
11
 
11
- def standard_search(routine, query, options, represent_with)
12
- model_klass = routine.send(:initial_relation).base_class
13
- OSU::AccessPolicy.require_action_allowed!(:search, current_api_user, model_klass)
14
- outputs = routine.call(query, options).outputs
12
+ def standard_search(routine, relation, represent_with, options={})
13
+ user = current_api_user
14
+ model_klass = relation.base_class
15
+ OSU::AccessPolicy.require_action_allowed!(:search, user, model_klass)
16
+ options = options.merge({
17
+ search_routine: routine,
18
+ search_relation: relation,
19
+ params: params
20
+ })
21
+ result = OpenStax::Utilities::KeywordSearchHandler.call(options)
22
+ return render_api_errors(result.errors) if result.errors.any?
23
+ outputs = result.outputs
24
+ outputs[:items].each do |item|
25
+ OSU::AccessPolicy.require_action_allowed!(:read, user, item)
26
+ end
15
27
  respond_with outputs, represent_with: represent_with
16
28
  end
17
29
 
18
- def standard_create(model, represent_with=nil, &block)
19
- standard_nested_create(model, nil, nil, represent_with, &block)
30
+ def standard_create(model, represent_with=nil)
31
+ model.class.transaction do
32
+ consume!(model, represent_with: represent_with)
33
+ yield model if block_given?
34
+ OSU::AccessPolicy.require_action_allowed!(:create, current_api_user, model)
35
+
36
+ if model.save
37
+ respond_with model, represent_with: represent_with, status: :created
38
+ else
39
+ render_api_errors(model.errors)
40
+ end
41
+ end
20
42
  end
21
43
 
22
44
  def standard_read(model, represent_with=nil)
@@ -25,6 +47,7 @@ module OpenStax
25
47
  end
26
48
 
27
49
  def standard_update(model, represent_with=nil)
50
+ # Must be able to update the record before and after the update itself
28
51
  OSU::AccessPolicy.require_action_allowed!(:update, current_api_user, model)
29
52
 
30
53
  model.class.transaction do
@@ -35,7 +58,7 @@ module OpenStax
35
58
  if model.save
36
59
  head :no_content
37
60
  else
38
- render json: model.errors, status: :unprocessable_entity
61
+ render_api_errors(model.errors)
39
62
  end
40
63
  end
41
64
  end
@@ -46,43 +69,53 @@ module OpenStax
46
69
  if model.destroy
47
70
  head :no_content
48
71
  else
49
- render json: model.errors, status: :unprocessable_entity
72
+ render_api_errors(model.errors)
50
73
  end
51
74
  end
52
75
 
53
76
  def standard_index(relation, represent_with)
54
77
  model_klass = relation.base_class
55
78
  OSU::AccessPolicy.require_action_allowed!(:index, current_api_user, model_klass)
56
- respond_with relation, represent_with: represent_with
79
+ relation.each do |item|
80
+ # Must be able to read each record
81
+ OSU::AccessPolicy.require_action_allowed!(:read, current_api_user, item)
82
+ end
83
+ respond_with(Lev::Outputs.new({items: relation}), {represent_with: represent_with})
57
84
  end
58
85
 
59
86
  def standard_sort(*args)
60
87
  raise NotYetImplemented
61
88
  end
62
89
 
63
- def standard_nested_create(model, container_association=nil,
64
- container=nil, represent_with=nil)
65
- if container_association && container
66
- OSU::AccessPolicy.require_action_allowed!(:update, current_api_user, container)
67
- model.send("#{container_association.to_s}=", container)
68
- end
90
+ def standard_nested_create(model, container_association,
91
+ container, represent_with=nil)
92
+ # Must be able to update the container
93
+ OSU::AccessPolicy.require_action_allowed!(:update, current_api_user, container)
94
+ model.send("#{container_association.to_s}=", container)
69
95
 
70
- # Unlike the implications of the representable README, "consume!" can
71
- # actually make changes to the database. See http://goo.gl/WVLBqA.
72
- # We do want to consume before checking the permissions so we can know
73
- # what we're dealing with, but if user doesn't have permission we don't
74
- # want to have changed the DB. Wrap in a transaction to protect ourselves.
75
- model.class.transaction do
76
- consume!(model, represent_with: represent_with)
77
- yield model if block_given?
78
- OSU::AccessPolicy.require_action_allowed!(:create, current_api_user, model)
96
+ standard_create(model, represent_with)
97
+ end
79
98
 
80
- if model.save
81
- respond_with model, represent_with: represent_with, status: :created
82
- else
83
- render json: model.errors, status: :unprocessable_entity
99
+ def render_api_errors(errors, status = :unprocessable_entity)
100
+ hash = {status: Rack::Utils.status_code(status)}
101
+ case errors
102
+ when ActiveModel::Errors, Lev::BetterActiveModelErrors
103
+ hash[:errors] = []
104
+ errors.each do |attribute, message|
105
+ hash[:errors] << {code: "#{attribute.to_s}_#{
106
+ message.to_s.gsub(/[\s-]/, '_').gsub(/[^\w]/, '')
107
+ }", message: errors.full_message(attribute, message)}
108
+ end
109
+ when Lev::Errors
110
+ hash[:errors] = errors.collect do |error|
111
+ {code: error.code, message: error.message, data: error.data}
112
+ end
113
+ else
114
+ hash[:errors] = [errors].flatten.collect do |error|
115
+ {code: error.to_s}
84
116
  end
85
117
  end
118
+ render json: hash, status: status
86
119
  end
87
120
 
88
121
  end
@@ -11,7 +11,8 @@ module OpenStax
11
11
  default: options.delete(:default))
12
12
 
13
13
  namespace api_namespace, defaults: {format: 'json'}.merge(options) do
14
- scope(module: version,
14
+ scope(except: [:new, :edit],
15
+ module: version,
15
16
  constraints: constraints) do
16
17
  get '/', to: '/apipie/apipies#index', defaults: {format: 'html',
17
18
  version: version.to_s}
@@ -1,5 +1,5 @@
1
1
  module OpenStax
2
2
  module Api
3
- VERSION = "3.0.1"
3
+ VERSION = "3.1.0"
4
4
  end
5
5
  end
@@ -1,7 +1,6 @@
1
1
  # Dummy routine for testing the abstract search representer
2
2
 
3
3
  class SearchUsers < OpenStax::Utilities::AbstractKeywordSearchRoutine
4
- self.initial_relation = User.unscoped
5
4
  self.search_proc = lambda { |with|
6
5
  with.keyword :username do |names|
7
6
  snames = to_string_array(names, append_wildcard: true)
@@ -72,4 +72,4 @@ Doorkeeper.configure do
72
72
  # Some applications require dynamic query parameters on their request_uri
73
73
  # set to true if you want this to be allowed
74
74
  # wildcard_redirect_uri false
75
- end
75
+ end
Binary file