openstax_api 3.0.1 → 3.1.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 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