haveapi 0.24.0 → 0.26.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
  SHA256:
3
- metadata.gz: 3aa75ace0c33e4332c2e01370c769d011a8b4bc1f143dcc72ff9a5019c55cf6c
4
- data.tar.gz: 86bce4af8c7d347bc11f6f3b840bc0d16ea4bac8d6bec827c1d6aea2f5a2c53f
3
+ metadata.gz: 8a2568eccc37711467dbd932eb6bb9c8fdf037dd82aca94f1d6791f563512f98
4
+ data.tar.gz: 5ef77a76b6f9c2457939542b650a3a73c0c67527845fca252d5ab80d61e4a390
5
5
  SHA512:
6
- metadata.gz: bb2e3d9d6f1b554b96896b1d3d45b18ac07e9a4d0bf6ea98a505125d741414a41c973f37d3717cac128f1ba58a1fc645d490f4fa8881e1394f2c0f9a56df7d68
7
- data.tar.gz: 8af545bdde9b7535460a99da6999a85c79c482b85c08a82af984e3acccd42d27df1dabb235a1f876280b6dd20763462a98789b1c44da6918e5bbfedcf3d7f48f
6
+ metadata.gz: c0c8903f4f81aaf485c22e8d056a70d691cda21a99b93b14ba9e4ebee3c2f518c2a08dfa5e0a3cc5b01057b9d94c3837bf3c0eee623c35ad2e8a37cc86ae93cd
7
+ data.tar.gz: 2b45b84c7e020ad680c7a6357bf5acc0893470a23cad01f830a4934bb61c33d8807b25e751f11a1fa41b631380f7b784b345939034ba897b07b77af475777f1c
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.24.0'
19
+ s.add_dependency 'haveapi-client', '~> 0.26.0'
20
20
  s.add_dependency 'json'
21
21
  s.add_dependency 'mail'
22
22
  s.add_dependency 'nesty', '~> 1.0'
@@ -197,13 +197,6 @@ module HaveAPI
197
197
 
198
198
  def build_route(prefix)
199
199
  route = @route || action_name.underscore
200
- if @route
201
- @route
202
- elsif action_name
203
- action_name.to_s.demodulize.underscore
204
- else
205
- to_s.demodulize.underscore
206
- end
207
200
 
208
201
  if !route.is_a?(String) && route.respond_to?(:call)
209
202
  route = route.call(resource)
@@ -323,7 +316,7 @@ module HaveAPI
323
316
  def validate!
324
317
  @params = validate
325
318
  rescue ValidationError => e
326
- error(e.message, e.to_hash)
319
+ error!(e.message, e.to_hash)
327
320
  end
328
321
 
329
322
  def authorized?(user)
@@ -379,11 +372,11 @@ module HaveAPI
379
372
  if tmp.empty?
380
373
  p e.message
381
374
  puts e.backtrace
382
- error('Server error occurred')
375
+ error!('Server error occurred')
383
376
  end
384
377
 
385
378
  unless tmp[:status]
386
- error(tmp[:message], {}, http_status: tmp[:http_status] || 500)
379
+ error!(tmp[:message], {}, http_status: tmp[:http_status] || 500)
387
380
  end
388
381
  end
389
382
 
@@ -555,7 +548,7 @@ module HaveAPI
555
548
  # @param ret [Hash] response
556
549
  # @param opts [Hash] options
557
550
  # @option opts [Integer] http_status HTTP status code sent to the client
558
- def ok(ret = {}, opts = {})
551
+ def ok!(ret = {}, opts = {})
559
552
  @http_status = opts[:http_status]
560
553
  throw(:return, ret)
561
554
  end
@@ -564,7 +557,7 @@ module HaveAPI
564
557
  # @param errs [Hash<Array>] parameter errors sent to the client
565
558
  # @param opts [Hash] options
566
559
  # @option opts [Integer] http_status HTTP status code sent to the client
567
- def error(msg, errs = {}, opts = {})
560
+ def error!(msg, errs = {}, opts = {})
568
561
  @message = msg
569
562
  @errors = errs
570
563
  @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
@@ -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
@@ -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)
@@ -1,4 +1,4 @@
1
1
  module HaveAPI
2
2
  PROTOCOL_VERSION = '2.0'.freeze
3
- VERSION = '0.24.0'.freeze
3
+ VERSION = '0.26.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.24.0
4
+ version: 0.26.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.24.0
47
+ version: 0.26.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.24.0
54
+ version: 0.26.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: json
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -343,7 +343,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
343
343
  - !ruby/object:Gem::Version
344
344
  version: '0'
345
345
  requirements: []
346
- rubygems_version: 3.5.9
346
+ rubygems_version: 3.5.22
347
347
  signing_key:
348
348
  specification_version: 4
349
349
  summary: Framework for creating self-describing APIs