onsi 1.3.1 → 2.0.1

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
  SHA256:
3
- metadata.gz: 57fb51dbc1c13dd74a18af1538b58c25f2feb39cb945688009f3d78c88520238
4
- data.tar.gz: 9612c74cc9fbaf207d49bbb30bcd60170c339a6d52f031efafee4578d99c32bf
3
+ metadata.gz: 866068b856dc9ce02d03c655448053a23e776388fdfa7569576411c8c92b01a4
4
+ data.tar.gz: 4c1eb3d324d81f30bf394120d3baf6822d83fa7e2b0ca1bc6124081ed71fb9ac
5
5
  SHA512:
6
- metadata.gz: 88a3da865f1c6843d659aba1a6213de659a9fd3eb6fa0e6492cc9ea91c8e94139ccaf98d54cded23bb9571bb7b5f658006ebafa33299bef1990e36408247f082
7
- data.tar.gz: 2254e6f2b098df7df588b3295dace8abedb8a707db96ed98c7465be2ff30d0d13104261866fbb9b6927fd9a2f3d502e42e3db463c5a00d6f040cbc48bf27037b
6
+ metadata.gz: ef0a1ee5e326bc2bfa9769882143be56d27b1bf0811d81aa0fce1a26a7c5df4c49d56961f84d1ce82c1c2dbe4e9afd06efab0bf6408940410c12ed2fa5cb46a1
7
+ data.tar.gz: 0201c618fcc64b69801ed66ff2fd85bea5f912027b53437aa65ffd7b83d67a8c8c111443b3481eee328c6d8e0b592df217886bab0da2d8291f22a5f4f085bbae
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## Version 2.0.0
4
+
5
+ ### unreleased
6
+
7
+ - Added pagination support
8
+
9
+ - Fix: Relationships without a valid value now render a `nil` data structure. This is a breaking change. To keep the old behavior you can use `legacy_relationship_render!`
10
+
11
+ ```ruby
12
+ api_render(:v1) do
13
+ legacy_relationship_render!
14
+ end
15
+ ```
16
+
3
17
  ## Version 1.3.0
4
18
 
5
19
  ### Released March 19, 2019
data/README.md CHANGED
@@ -126,3 +126,4 @@ class PeopleController < ApplicationController
126
126
  end
127
127
  end
128
128
  ```
129
+
@@ -4,6 +4,7 @@ require 'onsi/error_responder'
4
4
  require 'onsi/errors'
5
5
  require 'onsi/includes'
6
6
  require 'onsi/middleware'
7
+ require 'onsi/paginate'
7
8
  require 'onsi/model'
8
9
  require 'onsi/params'
9
10
  require 'onsi/resource'
@@ -34,6 +34,7 @@ module Onsi
34
34
  rescue_from Onsi::Params::RelationshipNotFound, with: :respond_missing_relationship_error_400
35
35
  rescue_from Onsi::Errors::UnknownVersionError, with: :respond_invalid_version_error_400
36
36
  rescue_from Onsi::Errors::IncludedParamError, with: :respond_included_param_error_400
37
+ rescue_from Onsi::Errors::PaginationError, with: :respond_pagination_error_400
37
38
  end
38
39
 
39
40
  ##
@@ -154,6 +155,18 @@ module Onsi
154
155
  render_error(response)
155
156
  end
156
157
 
158
+ ##
159
+ # @private
160
+ def respond_pagination_error_400(error)
161
+ response = ErrorResponse.new(400)
162
+ response.add(
163
+ 400,
164
+ 'pagination_error',
165
+ details: error.message
166
+ )
167
+ render_error(response)
168
+ end
169
+
157
170
  private
158
171
 
159
172
  def error_metadata(error)
@@ -55,5 +55,13 @@ module Onsi
55
55
  @path = path
56
56
  end
57
57
  end
58
+
59
+ ##
60
+ # Raised when there is an error with pagination
61
+ #
62
+ # @author Maddie Schipper
63
+ # @since 1.4.0
64
+ class PaginationError < BaseError
65
+ end
58
66
  end
59
67
  end
@@ -204,6 +204,10 @@ module Onsi
204
204
  end
205
205
  end
206
206
 
207
+ def legacy_relationship_render!
208
+ @legacy_relationship_render = true
209
+ end
210
+
207
211
  private
208
212
 
209
213
  def render_relationship_entry(object, key, value, rels)
@@ -224,6 +228,30 @@ module Onsi
224
228
  end
225
229
 
226
230
  def format_relationship(relationship, value)
231
+ if @legacy_relationship_render == true
232
+ return legacy_format_relationship(relationship, value)
233
+ end
234
+
235
+ case relationship
236
+ when Enumerable
237
+ if relationship.empty?
238
+ return []
239
+ end
240
+
241
+ relationship.map { |v| { 'type' => value[:type].to_s, 'id' => v.to_s } }
242
+ else
243
+ unless relationship.to_s.present?
244
+ return nil
245
+ end
246
+
247
+ {
248
+ 'type' => value[:type].to_s,
249
+ 'id' => relationship.to_s
250
+ }
251
+ end
252
+ end
253
+
254
+ def legacy_format_relationship(relationship, value)
227
255
  case relationship
228
256
  when Array
229
257
  relationship.map { |v| { 'type' => value[:type].to_s, 'id' => v.to_s } }
@@ -0,0 +1,83 @@
1
+ require_relative 'errors'
2
+
3
+ module Onsi
4
+ ##
5
+ # Pagination handles cursor pagination.
6
+ #
7
+ # It handles setting up a cursor and ordering the query. The next cursor will be added to the
8
+ # response's meta object.
9
+ #
10
+ # @example Controller Action
11
+ # def index
12
+ # render_resource(Onsi::Paginate.perform(person.messages, 'messages', params))
13
+ # end
14
+ #
15
+ # @author Maddie Schipper
16
+ # @since 1.4.0
17
+ class Paginate
18
+ Result = Struct.new(:query, :params)
19
+
20
+ class << self
21
+ ##
22
+ # Modify the query based on the pagination options
23
+ #
24
+ # @param query [#reorder, #limit, #where, #last] The query to modify
25
+ #
26
+ # @param type [#to_s] The type of the pagination. This us used to verify the cursor
27
+ #
28
+ # @param params [#fetch] The request params.
29
+ #
30
+ # @return [Onsi::Paginate::Result]
31
+ def perform(query, type, params, options = {})
32
+ cursor_type = type.to_s
33
+ cursor_param = options.fetch(:cursor_param, :cursor)
34
+ max_per_page = options.fetch(:max_per_page, 100)
35
+ per_page_param = options.fetch(:per_page_param, :per_page)
36
+ cursor_generator = options.fetch(:cursor, lambda { |query, type| Paginate.cursor_for_query(query, type) })
37
+ cursor_offset = options.fetch(:offset, lambda { |query, type, cursor| Paginate.cursor_offset(query, type, cursor) })
38
+ order_by = options.fetch(:order_by, id: :asc)
39
+
40
+ per_page = params.fetch(per_page_param, 25).to_i
41
+ raise Onsi::Errors::PaginationError, "too many objects per page, max #{max_per_page}" if per_page > max_per_page
42
+
43
+ query = query.reorder(order_by)
44
+ query = query.limit(per_page)
45
+ query = cursor_offset.call(query, cursor_type, params.fetch(cursor_param)) if params.fetch(cursor_param, nil).present?
46
+
47
+ response_params = {}.tap do |rp|
48
+ rp[per_page_param] = per_page
49
+ value = cursor_generator.call(query, cursor_type)
50
+ rp[cursor_param] = value unless value.nil?
51
+ end
52
+
53
+ Result.new(query, response_params)
54
+ end
55
+
56
+ ##
57
+ # @private
58
+ def cursor_for_query(query, type)
59
+ obj = query.last
60
+
61
+ return nil if obj.nil?
62
+
63
+ Base64.strict_encode64([
64
+ type.to_s,
65
+ obj.id.to_s
66
+ ].join(':'))
67
+ end
68
+
69
+ ##
70
+ # @private
71
+ def cursor_offset(query, cursor_type, cursor)
72
+ type, id = Base64.strict_decode64(cursor).split(':', 2)
73
+ raise Onsi::Errors::PaginationError, 'invalid cursor type' unless cursor_type.to_s == type
74
+
75
+ query.where(id: (id.to_i + 1)...Float::INFINITY)
76
+ rescue ArgumentError => e
77
+ raise e if e.message.downcase != 'invalid base64'
78
+
79
+ raise Onsi::Errors::PaginationError, 'invalid cursor format'
80
+ end
81
+ end
82
+ end
83
+ end
@@ -1,4 +1,5 @@
1
1
  require_relative 'errors'
2
+ require_relative 'paginate'
2
3
 
3
4
  module Onsi
4
5
  ##
@@ -71,6 +72,8 @@ module Onsi
71
72
  case resource
72
73
  when Onsi::Resource
73
74
  resource
75
+ when Onsi::Paginate::Result
76
+ as_resource(resource.query, version)
74
77
  when Enumerable
75
78
  resource.map { |res| as_resource(res, version) }
76
79
  else
@@ -97,6 +100,9 @@ module Onsi
97
100
  end
98
101
  root[META_KEY] = {}.tap do |meta|
99
102
  meta[:count] = resources.count if resources.respond_to?(:count)
103
+ if resource.is_a?(Onsi::Paginate::Result)
104
+ meta[:pagination] = resource.params
105
+ end
100
106
  end
101
107
  end
102
108
  end
@@ -1,5 +1,5 @@
1
1
  module Onsi
2
2
  ##
3
3
  # The current version of Onsi
4
- VERSION = '1.3.1'.freeze
4
+ VERSION = '2.0.1'.freeze
5
5
  end
@@ -23,14 +23,14 @@ Gem::Specification.new do |spec|
23
23
  spec.required_ruby_version = '>= 2.3'
24
24
 
25
25
  spec.add_dependency 'addressable', '>= 2.5', '< 3.0'
26
- spec.add_dependency 'rails', '>= 5.0', '< 6.0'
26
+ spec.add_dependency 'rails', '>= 5.0', '< 7.0'
27
27
 
28
- spec.add_development_dependency 'bundler', '>= 1.16', '< 3.0'
29
- spec.add_development_dependency 'database_cleaner', '~> 1.7.0'
30
- spec.add_development_dependency 'pry', '~> 0.11.3'
31
- spec.add_development_dependency 'rake', '~> 10.0'
32
- spec.add_development_dependency 'rspec-rails', '~> 3.7.2'
33
- spec.add_development_dependency 'simplecov', '~> 0.15'
34
- spec.add_development_dependency 'sqlite3', '~> 1.3.10'
35
- spec.add_development_dependency 'yard', '~> 0.9.16'
28
+ spec.add_development_dependency 'bundler'
29
+ spec.add_development_dependency 'database_cleaner'
30
+ spec.add_development_dependency 'pry'
31
+ spec.add_development_dependency 'rake'
32
+ spec.add_development_dependency 'rspec-rails'
33
+ spec.add_development_dependency 'simplecov', '< 0.18' # >= .18 breaks the test reporter
34
+ spec.add_development_dependency 'sqlite3'
35
+ spec.add_development_dependency 'yard'
36
36
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: onsi
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maddie Schipper
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-03-20 00:00:00.000000000 Z
11
+ date: 2020-03-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -39,7 +39,7 @@ dependencies:
39
39
  version: '5.0'
40
40
  - - "<"
41
41
  - !ruby/object:Gem::Version
42
- version: '6.0'
42
+ version: '7.0'
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
@@ -49,125 +49,119 @@ dependencies:
49
49
  version: '5.0'
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
- version: '6.0'
52
+ version: '7.0'
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: bundler
55
55
  requirement: !ruby/object:Gem::Requirement
56
56
  requirements:
57
57
  - - ">="
58
58
  - !ruby/object:Gem::Version
59
- version: '1.16'
60
- - - "<"
61
- - !ruby/object:Gem::Version
62
- version: '3.0'
59
+ version: '0'
63
60
  type: :development
64
61
  prerelease: false
65
62
  version_requirements: !ruby/object:Gem::Requirement
66
63
  requirements:
67
64
  - - ">="
68
65
  - !ruby/object:Gem::Version
69
- version: '1.16'
70
- - - "<"
71
- - !ruby/object:Gem::Version
72
- version: '3.0'
66
+ version: '0'
73
67
  - !ruby/object:Gem::Dependency
74
68
  name: database_cleaner
75
69
  requirement: !ruby/object:Gem::Requirement
76
70
  requirements:
77
- - - "~>"
71
+ - - ">="
78
72
  - !ruby/object:Gem::Version
79
- version: 1.7.0
73
+ version: '0'
80
74
  type: :development
81
75
  prerelease: false
82
76
  version_requirements: !ruby/object:Gem::Requirement
83
77
  requirements:
84
- - - "~>"
78
+ - - ">="
85
79
  - !ruby/object:Gem::Version
86
- version: 1.7.0
80
+ version: '0'
87
81
  - !ruby/object:Gem::Dependency
88
82
  name: pry
89
83
  requirement: !ruby/object:Gem::Requirement
90
84
  requirements:
91
- - - "~>"
85
+ - - ">="
92
86
  - !ruby/object:Gem::Version
93
- version: 0.11.3
87
+ version: '0'
94
88
  type: :development
95
89
  prerelease: false
96
90
  version_requirements: !ruby/object:Gem::Requirement
97
91
  requirements:
98
- - - "~>"
92
+ - - ">="
99
93
  - !ruby/object:Gem::Version
100
- version: 0.11.3
94
+ version: '0'
101
95
  - !ruby/object:Gem::Dependency
102
96
  name: rake
103
97
  requirement: !ruby/object:Gem::Requirement
104
98
  requirements:
105
- - - "~>"
99
+ - - ">="
106
100
  - !ruby/object:Gem::Version
107
- version: '10.0'
101
+ version: '0'
108
102
  type: :development
109
103
  prerelease: false
110
104
  version_requirements: !ruby/object:Gem::Requirement
111
105
  requirements:
112
- - - "~>"
106
+ - - ">="
113
107
  - !ruby/object:Gem::Version
114
- version: '10.0'
108
+ version: '0'
115
109
  - !ruby/object:Gem::Dependency
116
110
  name: rspec-rails
117
111
  requirement: !ruby/object:Gem::Requirement
118
112
  requirements:
119
- - - "~>"
113
+ - - ">="
120
114
  - !ruby/object:Gem::Version
121
- version: 3.7.2
115
+ version: '0'
122
116
  type: :development
123
117
  prerelease: false
124
118
  version_requirements: !ruby/object:Gem::Requirement
125
119
  requirements:
126
- - - "~>"
120
+ - - ">="
127
121
  - !ruby/object:Gem::Version
128
- version: 3.7.2
122
+ version: '0'
129
123
  - !ruby/object:Gem::Dependency
130
124
  name: simplecov
131
125
  requirement: !ruby/object:Gem::Requirement
132
126
  requirements:
133
- - - "~>"
127
+ - - "<"
134
128
  - !ruby/object:Gem::Version
135
- version: '0.15'
129
+ version: '0.18'
136
130
  type: :development
137
131
  prerelease: false
138
132
  version_requirements: !ruby/object:Gem::Requirement
139
133
  requirements:
140
- - - "~>"
134
+ - - "<"
141
135
  - !ruby/object:Gem::Version
142
- version: '0.15'
136
+ version: '0.18'
143
137
  - !ruby/object:Gem::Dependency
144
138
  name: sqlite3
145
139
  requirement: !ruby/object:Gem::Requirement
146
140
  requirements:
147
- - - "~>"
141
+ - - ">="
148
142
  - !ruby/object:Gem::Version
149
- version: 1.3.10
143
+ version: '0'
150
144
  type: :development
151
145
  prerelease: false
152
146
  version_requirements: !ruby/object:Gem::Requirement
153
147
  requirements:
154
- - - "~>"
148
+ - - ">="
155
149
  - !ruby/object:Gem::Version
156
- version: 1.3.10
150
+ version: '0'
157
151
  - !ruby/object:Gem::Dependency
158
152
  name: yard
159
153
  requirement: !ruby/object:Gem::Requirement
160
154
  requirements:
161
- - - "~>"
155
+ - - ">="
162
156
  - !ruby/object:Gem::Version
163
- version: 0.9.16
157
+ version: '0'
164
158
  type: :development
165
159
  prerelease: false
166
160
  version_requirements: !ruby/object:Gem::Requirement
167
161
  requirements:
168
- - - "~>"
162
+ - - ">="
169
163
  - !ruby/object:Gem::Version
170
- version: 0.9.16
164
+ version: '0'
171
165
  description: Format JSON API responses and parse inbound requests.
172
166
  email:
173
167
  - me@maddiesch.com
@@ -202,6 +196,7 @@ files:
202
196
  - lib/onsi/middleware.rb
203
197
  - lib/onsi/middleware/cors_headers.rb
204
198
  - lib/onsi/model.rb
199
+ - lib/onsi/paginate.rb
205
200
  - lib/onsi/params.rb
206
201
  - lib/onsi/params_parse_operation.rb
207
202
  - lib/onsi/params_parser.rb