onsi 1.3.1 → 2.0.1

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