haveapi 0.23.7 → 0.25.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
  SHA256:
3
- metadata.gz: 5f3a7731a5016c5b5ea0a801021cb5edb150f64543119c1c8b8bf54a4c7789a5
4
- data.tar.gz: 530120c4fec361fb3581e7a9fc2b2648c3ff33eaabd07e9bfa4bf9b4fdd75656
3
+ metadata.gz: 3e3abf24d477cb53a7654a236f2daf3abadd8b153d3017ea159dbe1dd072fd2b
4
+ data.tar.gz: 406961707ce35f79c6ee6f2835697143f2b5a4e42e86f433949d08f98b546644
5
5
  SHA512:
6
- metadata.gz: 1436deb0578ff83809622a9857d8e2e56bd2f52d33b69ea5718c1cf220fa78d5995baa89eb073da5b24b11631aed2edb14fac77de3fe339ef94e7cce4d6abe81
7
- data.tar.gz: b4c743579d26331685d0602d37168a887d88ca01449e5ae084fe053bc8775d36d1c410c6d596e42c4c717c94d85ab810000be0c22d3e1a8553eae3faa9fda512
6
+ metadata.gz: bdddd4eb49bc38b54c799b2cd57668f550a38212f1b735f1aad6238bb93f4a7d3b0b1bc52ff09055d0247afd70fa6fe156b41d7c99bc20317bc470d22ae9f744
7
+ data.tar.gz: 0120c81aa41cbc7a32d039fb4c645b2fa1ca510d91093c813e4e3b8802e69393e2641af63c99287fbb8b75f56021461e1c2eaf67351874f7ee8865195af7cac6
data/README.md CHANGED
@@ -138,7 +138,7 @@ module MyAPI
138
138
 
139
139
  # Execute action, return the list
140
140
  def exec
141
- query.limit(input[:limit]).offset(input[:offset])
141
+ with_pagination(query)
142
142
  end
143
143
  end
144
144
 
@@ -178,9 +178,9 @@ module MyAPI
178
178
  user = ::User.new(input)
179
179
 
180
180
  if user.save
181
- ok(user)
181
+ ok!(user)
182
182
  else
183
- error('save failed', user.errors.to_hash)
183
+ error!('save failed', user.errors.to_hash)
184
184
  end
185
185
  end
186
186
  end
data/haveapi.gemspec CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
16
16
 
17
17
  s.add_dependency 'activesupport', '>= 7.1'
18
18
  s.add_dependency 'github-markdown'
19
- s.add_dependency 'haveapi-client', '~> 0.23.4'
19
+ s.add_dependency 'haveapi-client', '~> 0.25.0'
20
20
  s.add_dependency 'json'
21
21
  s.add_dependency 'mail'
22
22
  s.add_dependency 'nesty', '~> 1.0'
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
24
24
  s.add_dependency 'rake'
25
25
  s.add_dependency 'redcarpet', '~> 3.6'
26
26
  s.add_dependency 'require_all', '~> 2.0.0'
27
- s.add_dependency 'sinatra', '~> 3.1.0'
28
- s.add_dependency 'sinatra-contrib', '~> 3.1.0'
29
- s.add_dependency 'tilt', '~> 2.3.0'
27
+ s.add_dependency 'sinatra', '~> 4.0'
28
+ s.add_dependency 'sinatra-contrib', '~> 4.0'
29
+ s.add_dependency 'tilt', '~> 2.4'
30
30
  end
@@ -323,7 +323,7 @@ module HaveAPI
323
323
  def validate!
324
324
  @params = validate
325
325
  rescue ValidationError => e
326
- error(e.message, e.to_hash)
326
+ error!(e.message, e.to_hash)
327
327
  end
328
328
 
329
329
  def authorized?(user)
@@ -379,11 +379,11 @@ module HaveAPI
379
379
  if tmp.empty?
380
380
  p e.message
381
381
  puts e.backtrace
382
- error('Server error occurred')
382
+ error!('Server error occurred')
383
383
  end
384
384
 
385
385
  unless tmp[:status]
386
- error(tmp[:message], {}, http_status: tmp[:http_status] || 500)
386
+ error!(tmp[:message], {}, http_status: tmp[:http_status] || 500)
387
387
  end
388
388
  end
389
389
 
@@ -555,7 +555,7 @@ module HaveAPI
555
555
  # @param ret [Hash] response
556
556
  # @param opts [Hash] options
557
557
  # @option opts [Integer] http_status HTTP status code sent to the client
558
- def ok(ret = {}, opts = {})
558
+ def ok!(ret = {}, opts = {})
559
559
  @http_status = opts[:http_status]
560
560
  throw(:return, ret)
561
561
  end
@@ -564,7 +564,7 @@ module HaveAPI
564
564
  # @param errs [Hash<Array>] parameter errors sent to the client
565
565
  # @param opts [Hash] options
566
566
  # @option opts [Integer] http_status HTTP status code sent to the client
567
- def error(msg, errs = {}, opts = {})
567
+ def error!(msg, errs = {}, opts = {})
568
568
  @message = msg
569
569
  @errors = errs
570
570
  @http_status = opts[:http_status]
@@ -11,11 +11,11 @@ module HaveAPI
11
11
  class ActionState
12
12
  # Return an array of objects representing actions that are pending completion.
13
13
  # @param [Object] user
14
- # @param [Integer] offset
14
+ # @param [Integer] from_id
15
15
  # @param [Integer] limit
16
16
  # @param [Symbol] order (:newest or :oldest)
17
17
  # @return [Array<ActionState>]
18
- def self.list_pending(user, offset, limit, order)
18
+ def self.list_pending(user, from_id, limit, order)
19
19
  raise NotImplementedError
20
20
  end
21
21
 
@@ -2,9 +2,9 @@ module HaveAPI::Actions
2
2
  module Paginable
3
3
  def self.included(action)
4
4
  action.input do
5
- integer :offset, label: 'Offset', desc: 'The offset of the first object',
6
- number: { min: 0 }
7
- integer :limit, label: 'Limit', desc: 'The number of objects to retrieve',
5
+ integer :from_id, label: 'From ID', desc: 'List objects with greater/lesser ID',
6
+ number: { min: 0 }
7
+ integer :limit, label: 'Limit', desc: 'Number of objects to retrieve',
8
8
  number: { min: 0 }
9
9
  end
10
10
  end
@@ -114,7 +114,7 @@ module HaveAPI::Authentication
114
114
 
115
115
  def authenticate(request)
116
116
  tokens = [
117
- request['access_token'],
117
+ request.params['access_token'],
118
118
  token_from_authorization_header(request),
119
119
  token_from_haveapi_header(request)
120
120
  ].compact
@@ -159,7 +159,7 @@ module HaveAPI::Authentication
159
159
  # @param request [Sinatra::Request]
160
160
  # @return [String]
161
161
  def token(request)
162
- request[config.class.query_parameter] || request.env[header_to_env]
162
+ request.params[config.class.query_parameter] || request.env[header_to_env]
163
163
  end
164
164
 
165
165
  def describe
@@ -227,11 +227,11 @@ module HaveAPI::Authentication
227
227
  input:
228
228
  ), ActionResult.new)
229
229
  rescue HaveAPI::AuthenticationError => e
230
- error(e.message)
230
+ error!(e.message)
231
231
  end
232
232
 
233
233
  unless result.ok?
234
- error(result.error || 'invalid authentication credentials')
234
+ error!(result.error || 'invalid authentication credentials')
235
235
  end
236
236
 
237
237
  {
@@ -260,9 +260,9 @@ module HaveAPI::Authentication
260
260
  ), ActionResult.new)
261
261
 
262
262
  if result.ok?
263
- ok
263
+ ok!
264
264
  else
265
- error(result.error || 'revoke failed')
265
+ error!(result.error || 'revoke failed')
266
266
  end
267
267
  end
268
268
  end
@@ -290,7 +290,7 @@ module HaveAPI::Authentication
290
290
  if result.ok?
291
291
  { valid_to: result.valid_to }
292
292
  else
293
- error(result.error || 'renew failed')
293
+ error!(result.error || 'renew failed')
294
294
  end
295
295
  end
296
296
  end
@@ -324,11 +324,11 @@ module HaveAPI::Authentication
324
324
  token: input[:token]
325
325
  ), ActionResult.new)
326
326
  rescue HaveAPI::AuthenticationError => e
327
- error(e.message)
327
+ error!(e.message)
328
328
  end
329
329
 
330
330
  unless result.ok?
331
- error(result.error || 'authentication failed')
331
+ error!(result.error || 'authentication failed')
332
332
  end
333
333
 
334
334
  {
@@ -41,6 +41,47 @@ module HaveAPI::ModelAdapters
41
41
  end
42
42
  end
43
43
 
44
+ # Apply pagination on query in ascending order
45
+ # @param q [ActiveRecord::Relation] query to apply pagination on
46
+ def with_asc_pagination(q = nil)
47
+ ar_with_pagination(q, check: true) do |query, from_id|
48
+ query.where("`#{self.class.model.table_name}`.`#{self.class.model.primary_key}` > ?", from_id)
49
+ end
50
+ end
51
+
52
+ # Apply pagination on query in descending order
53
+ # @param q [ActiveRecord::Relation] query to apply pagination on
54
+ def with_desc_pagination(q = nil)
55
+ ar_with_pagination(q, check: true) do |query, from_id|
56
+ query.where("`#{self.class.model.table_name}`.`#{self.class.model.primary_key}` < ?", from_id)
57
+ end
58
+ end
59
+
60
+ alias with_pagination with_asc_pagination
61
+
62
+ # @param q [ActiveRecord::Relation] query to apply pagination on
63
+ # @param parameter [Symbol] input parameter used for pagination
64
+ # @param check [Boolean] raise if the model does not have a simple primary key
65
+ # @yieldparam q [ActiveRecord::Relation] query to apply pagination on
66
+ # @yieldparam parameter [any] value of the input parameter
67
+ def ar_with_pagination(q, parameter: :from_id, check: false)
68
+ pk = self.class.model.primary_key
69
+
70
+ if check && !pk.is_a?(String)
71
+ raise 'only simple primary key is supported, ' \
72
+ "#{self.class.model} has a composite primary key (#{pk.join(', ')})"
73
+ end
74
+
75
+ q ||= self.class.model.all
76
+
77
+ paginable = input[parameter]
78
+ limit = input[:limit]
79
+
80
+ q = yield(q, paginable) if paginable
81
+ q = q.limit(limit) if limit
82
+ q
83
+ end
84
+
44
85
  # Parse includes sent by the user and return them
45
86
  # in an array of symbols and hashes.
46
87
  def ar_parse_includes(raw)
@@ -154,6 +154,13 @@ module HaveAPI
154
154
  @params.detect { |p| p.name == name }.patch(changes)
155
155
  end
156
156
 
157
+ def remove(name)
158
+ i = @params.index { |p| p.name == name }
159
+ raise "Parameter #{name.inspect} not found" if i.nil?
160
+
161
+ @params.delete_at(i)
162
+ end
163
+
157
164
  # Action returns custom data.
158
165
  def custom(name, **kwargs, &block)
159
166
  add_param(name, apply(kwargs, type: Custom, clean: block))
@@ -63,7 +63,7 @@ module HaveAPI::Resources
63
63
  def exec
64
64
  actions = @context.server.action_state.list_pending(
65
65
  current_user,
66
- input[:offset],
66
+ input[:from_id],
67
67
  input[:limit],
68
68
  input[:order].to_sym
69
69
  )
@@ -106,7 +106,7 @@ module HaveAPI::Resources
106
106
  id: params[:action_state_id]
107
107
  )
108
108
 
109
- error('action state not found') unless state.valid?
109
+ error!('action state not found') unless state.valid?
110
110
 
111
111
  if state.finished? || (Time.now - t) >= input[:timeout]
112
112
  return state_to_hash(state)
@@ -145,7 +145,7 @@ module HaveAPI::Resources
145
145
 
146
146
  return state_to_hash(state) if state.valid?
147
147
 
148
- error('action state not found')
148
+ error!('action state not found')
149
149
  end
150
150
  end
151
151
 
@@ -164,7 +164,7 @@ module HaveAPI::Resources
164
164
  id: params[:action_state_id]
165
165
  )
166
166
 
167
- error('action state not found') unless state.valid?
167
+ error!('action state not found') unless state.valid?
168
168
 
169
169
  ret = state.cancel
170
170
 
@@ -172,13 +172,13 @@ module HaveAPI::Resources
172
172
  @state_id = ret
173
173
 
174
174
  elsif ret
175
- ok
175
+ ok!
176
176
 
177
177
  else
178
- error('cancellation failed')
178
+ error!('cancellation failed')
179
179
  end
180
180
  rescue RuntimeError, NotImplementedError => e
181
- error(e.message)
181
+ error!(e.message)
182
182
  end
183
183
 
184
184
  attr_reader :state_id
@@ -74,11 +74,11 @@ module HaveAPI
74
74
  return unless request.env['HTTP_ORIGIN'] && request.env['HTTP_ACCESS_CONTROL_REQUEST_METHOD']
75
75
 
76
76
  halt 200, {
77
- 'Access-Control-Allow-Origin' => '*',
78
- 'Access-Control-Allow-Methods' => 'GET,POST,OPTIONS,PATCH,PUT,DELETE',
79
- 'Access-Control-Allow-Credentials' => 'false',
80
- 'Access-Control-Allow-Headers' => settings.api_server.allowed_headers,
81
- 'Access-Control-Max-Age' => (60 * 60).to_s
77
+ 'access-control-allow-origin' => '*',
78
+ 'access-control-allow-methods' => 'GET,POST,OPTIONS,PATCH,PUT,DELETE',
79
+ 'access-control-allow-credentials' => 'false',
80
+ 'access-control-allow-headers' => settings.api_server.allowed_headers,
81
+ 'access-control-max-age' => (60 * 60).to_s
82
82
  }, ''
83
83
  end
84
84
 
@@ -94,7 +94,7 @@ module HaveAPI
94
94
  def require_auth!
95
95
  report_error(
96
96
  401,
97
- { 'WWW-Authenticate' => 'Basic realm="Restricted Area"' },
97
+ { 'www-authenticate' => 'Basic realm="Restricted Area"' },
98
98
  'Action requires user to authenticate'
99
99
  )
100
100
  end
@@ -166,13 +166,19 @@ module HaveAPI
166
166
  ret += "<h5>#{name.to_s.capitalize}</h5>"
167
167
  ret += '<dl>'
168
168
  opts.each do |k, v|
169
- ret += "<dt>#{k}</dt><dd>#{v}</dd>"
169
+ ret += "<dt>#{k}</dt><dd>#{escape_html(v.to_s)}</dd>"
170
170
  end
171
171
  ret += '</dl>'
172
172
  end
173
173
 
174
174
  ret
175
175
  end
176
+
177
+ def escape_html(v)
178
+ return '' if v.nil?
179
+
180
+ CGI.escapeHTML(v.to_s)
181
+ end
176
182
  end
177
183
 
178
184
  def initialize(module_name = HaveAPI.module_name)
@@ -231,8 +237,8 @@ module HaveAPI
231
237
 
232
238
  before do
233
239
  if request.env['HTTP_ORIGIN']
234
- headers 'Access-Control-Allow-Origin' => '*',
235
- 'Access-Control-Allow-Credentials' => 'false'
240
+ headers 'access-control-allow-origin' => '*',
241
+ 'access-control-allow-credentials' => 'false'
236
242
  end
237
243
  end
238
244
 
@@ -468,8 +474,6 @@ module HaveAPI
468
474
  authenticated?(v)
469
475
  end
470
476
 
471
- request.body.rewind
472
-
473
477
  begin
474
478
  body = request.body.read
475
479
 
@@ -42,7 +42,7 @@ module HaveAPI::Spec
42
42
  method(action.http_method).call(
43
43
  path,
44
44
  params && params.to_json,
45
- { 'Content-Type' => 'application/json' }
45
+ { 'content-type' => 'application/json' }
46
46
  )
47
47
 
48
48
  else
@@ -51,7 +51,7 @@ module HaveAPI::Spec
51
51
  method(http_method).call(
52
52
  path,
53
53
  params && params.to_json,
54
- { 'Content-Type' => 'application/json' }
54
+ { 'content-type' => 'application/json' }
55
55
  )
56
56
  end
57
57
  end
@@ -1,4 +1,4 @@
1
1
  module HaveAPI
2
2
  PROTOCOL_VERSION = '2.0'.freeze
3
- VERSION = '0.23.7'.freeze
3
+ VERSION = '0.25.0'.freeze
4
4
  end
@@ -54,8 +54,8 @@
54
54
  <td><%= info[:required] ? 'yes' : 'no' %></td>
55
55
  <td><%= format_param_type(info) %></td>
56
56
  <td><%= format_validators(info[:validators]) %></td>
57
- <td><%= info[:default] == :_nil ? '' : info[:default] %></td>
58
- <td><%= info[:description] %></td>
57
+ <td><%= info[:default] == :_nil ? '' : escape_html(info[:default]) %></td>
58
+ <td><%= escape_html(info[:description]) %></td>
59
59
  </tr>
60
60
  <% end %>
61
61
  </table>
@@ -86,18 +86,13 @@
86
86
  <td><%= info[:label] %></td>
87
87
  <td><%= param %></td>
88
88
  <td><%= format_param_type(info) %></td>
89
- <td><%= info[:description] %></td>
89
+ <td><%= escape_html(info[:description]) %></td>
90
90
  </tr>
91
91
  <% end %>
92
92
  </table>
93
93
  <% end %>
94
94
  </div>
95
95
 
96
- <div class="action-self-description">
97
- <h4>Self-description</h4>
98
- <pre><code><%= JSON.pretty_generate(info) %></code></pre>
99
- </div>
100
-
101
96
  <% unless info[:examples].empty? %>
102
97
  <h4>Examples</h4>
103
98
  <% info[:examples].each_with_index do |example, i| %>
@@ -111,33 +111,5 @@ nojsTabs({
111
111
 
112
112
  event.preventDefault();
113
113
  });
114
-
115
- // Show/hide button for help messages
116
- $('.action .action-self-description').each(function(i, el){
117
- // replace text by show/hide button
118
- var div = $(el);
119
- var pre = div.find('pre');
120
- var h4 = div.find('h4');
121
- h4.text('');
122
-
123
- h4.append(
124
- $('<button>')
125
- .attr('type', 'button')
126
- .addClass('btn btn-primary btn-sm')
127
- .text('Show self-description')
128
- .click(function(event) {
129
- var button = $(this);
130
- button.toggleClass('active');
131
- pre.toggle('fast');
132
-
133
- if(button.hasClass('active'))
134
- button.text('Hide self-description');
135
- else
136
- button.text('Show self-description');
137
- })
138
- );
139
-
140
- $(el).find('pre').hide();
141
- });
142
114
  });
143
115
  </script>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haveapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.23.7
4
+ version: 0.25.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakub Skokan
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.23.4
47
+ version: 0.25.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.23.4
54
+ version: 0.25.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: json
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -156,42 +156,42 @@ dependencies:
156
156
  requirements:
157
157
  - - "~>"
158
158
  - !ruby/object:Gem::Version
159
- version: 3.1.0
159
+ version: '4.0'
160
160
  type: :runtime
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
- version: 3.1.0
166
+ version: '4.0'
167
167
  - !ruby/object:Gem::Dependency
168
168
  name: sinatra-contrib
169
169
  requirement: !ruby/object:Gem::Requirement
170
170
  requirements:
171
171
  - - "~>"
172
172
  - !ruby/object:Gem::Version
173
- version: 3.1.0
173
+ version: '4.0'
174
174
  type: :runtime
175
175
  prerelease: false
176
176
  version_requirements: !ruby/object:Gem::Requirement
177
177
  requirements:
178
178
  - - "~>"
179
179
  - !ruby/object:Gem::Version
180
- version: 3.1.0
180
+ version: '4.0'
181
181
  - !ruby/object:Gem::Dependency
182
182
  name: tilt
183
183
  requirement: !ruby/object:Gem::Requirement
184
184
  requirements:
185
185
  - - "~>"
186
186
  - !ruby/object:Gem::Version
187
- version: 2.3.0
187
+ version: '2.4'
188
188
  type: :runtime
189
189
  prerelease: false
190
190
  version_requirements: !ruby/object:Gem::Requirement
191
191
  requirements:
192
192
  - - "~>"
193
193
  - !ruby/object:Gem::Version
194
- version: 2.3.0
194
+ version: '2.4'
195
195
  description: Framework for creating self-describing APIs
196
196
  email: jakub.skokan@vpsfree.cz
197
197
  executables: []