haveapi 0.12.1 → 0.13.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/doc/protocol.md +27 -10
  3. data/haveapi.gemspec +1 -2
  4. data/lib/haveapi/action.rb +23 -7
  5. data/lib/haveapi/actions/default.rb +3 -3
  6. data/lib/haveapi/authentication/base.rb +19 -1
  7. data/lib/haveapi/authentication/basic/provider.rb +7 -1
  8. data/lib/haveapi/authentication/chain.rb +10 -12
  9. data/lib/haveapi/authentication/token/action_config.rb +53 -0
  10. data/lib/haveapi/authentication/token/action_request.rb +23 -0
  11. data/lib/haveapi/authentication/token/action_result.rb +42 -0
  12. data/lib/haveapi/authentication/token/config.rb +115 -0
  13. data/lib/haveapi/authentication/token/provider.rb +259 -81
  14. data/lib/haveapi/authentication/token.rb +9 -0
  15. data/lib/haveapi/client_examples/curl.rb +3 -3
  16. data/lib/haveapi/client_examples/fs_client.rb +3 -3
  17. data/lib/haveapi/client_examples/http.rb +7 -7
  18. data/lib/haveapi/client_examples/js_client.rb +1 -1
  19. data/lib/haveapi/client_examples/php_client.rb +1 -1
  20. data/lib/haveapi/client_examples/ruby_cli.rb +1 -1
  21. data/lib/haveapi/client_examples/ruby_client.rb +1 -1
  22. data/lib/haveapi/context.rb +13 -13
  23. data/lib/haveapi/example.rb +3 -3
  24. data/lib/haveapi/exceptions.rb +2 -0
  25. data/lib/haveapi/extensions/exception_mailer.rb +8 -1
  26. data/lib/haveapi/model_adapters/active_record.rb +6 -6
  27. data/lib/haveapi/parameters/resource.rb +10 -10
  28. data/lib/haveapi/params.rb +1 -1
  29. data/lib/haveapi/resource.rb +18 -10
  30. data/lib/haveapi/resources/action_state.rb +2 -2
  31. data/lib/haveapi/route.rb +4 -3
  32. data/lib/haveapi/server.rb +7 -7
  33. data/lib/haveapi/spec/mock_action.rb +3 -3
  34. data/lib/haveapi/spec/spec_methods.rb +8 -8
  35. data/lib/haveapi/version.rb +2 -2
  36. data/lib/haveapi/views/version_page.erb +2 -2
  37. data/shell.nix +1 -1
  38. metadata +9 -5
  39. data/lib/haveapi/authentication/token/resources.rb +0 -110
@@ -114,7 +114,7 @@ module HaveAPI::ModelAdapters
114
114
  def self.used_by(action)
115
115
  action.meta(:object) do
116
116
  output do
117
- custom :url_params, label: 'URL parameters',
117
+ custom :path_params, label: 'URL parameters',
118
118
  desc: 'An array of parameters needed to resolve URL to this object'
119
119
  bool :resolved, label: 'Resolved', desc: 'True if the association is resolved'
120
120
  end
@@ -172,14 +172,14 @@ END
172
172
  if @context.action.name.demodulize == 'Index' \
173
173
  && !@context.action.resolve \
174
174
  && res.const_defined?(:Show)
175
- params = res::Show.resolve_url_params(@object)
175
+ params = res::Show.resolve_path_params(@object)
176
176
 
177
177
  else
178
- params = @context.action.resolve_url_params(@object)
178
+ params = @context.action.resolve_path_params(@object)
179
179
  end
180
180
 
181
181
  {
182
- url_params: params.is_a?(Array) ? params : [params],
182
+ path_params: params.is_a?(Array) ? params : [params],
183
183
  resolved: true
184
184
  }
185
185
  end
@@ -195,7 +195,7 @@ END
195
195
  res_show = param.show_action
196
196
  res_output = res_show.output
197
197
 
198
- args = res_show.resolve_url_params(val)
198
+ args = res_show.resolve_path_params(val)
199
199
 
200
200
  if includes_include?(param.name)
201
201
  push_cls = @context.action
@@ -234,7 +234,7 @@ END
234
234
  param.value_id => val.send(res_output[param.value_id].db_name),
235
235
  param.value_label => val.send(res_output[param.value_label].db_name),
236
236
  _meta: {
237
- :url_params => args.is_a?(Array) ? args : [args],
237
+ :path_params => args.is_a?(Array) ? args : [args],
238
238
  :resolved => false
239
239
  }
240
240
  }
@@ -8,8 +8,8 @@ module HaveAPI::Parameters
8
8
  db_name: nil, fetch: nil)
9
9
  @resource = resource
10
10
  @resource_path = build_resource_path(resource)
11
- @name = name || resource.to_s.demodulize.underscore.to_sym
12
- @label = label || (name && name.to_s.capitalize) || resource.to_s.demodulize
11
+ @name = name || resource.resource_name.underscore.to_sym
12
+ @label = label || (name && name.to_s.capitalize) || resource.resource_name
13
13
  @desc = desc
14
14
  @choices = choices || @resource::Index
15
15
  @value_id = value_id
@@ -42,15 +42,15 @@ module HaveAPI::Parameters
42
42
  end
43
43
 
44
44
  def describe(context)
45
- val_url = context.url_for(
45
+ val_path = context.path_for(
46
46
  @resource::Show,
47
- context.endpoint && context.action_prepare && context.layout == :object && context.call_url_params(context.action, context.action_prepare)
47
+ context.endpoint && context.action_prepare && context.layout == :object && context.call_path_params(context.action, context.action_prepare)
48
48
  )
49
49
  val_method = @resource::Index.http_method.to_s.upcase
50
50
 
51
- choices_url = context.url_for(
51
+ choices_path = context.path_for(
52
52
  @choices,
53
- context.endpoint && context.layout == :object && context.call_url_params(context.action, context.action_prepare)
53
+ context.endpoint && context.layout == :object && context.call_path_params(context.action, context.action_prepare)
54
54
  )
55
55
  choices_method = @choices.http_method.to_s.upcase
56
56
 
@@ -63,14 +63,14 @@ module HaveAPI::Parameters
63
63
  value_id: @value_id,
64
64
  value_label: @value_label,
65
65
  value: context.action_prepare && {
66
- url: val_url,
66
+ path: val_path,
67
67
  method: val_method,
68
- help: "#{val_url}?method=#{val_method}",
68
+ help: "#{val_path}?method=#{val_method}",
69
69
  },
70
70
  choices: {
71
- url: choices_url,
71
+ path: choices_path,
72
72
  method: choices_method,
73
- help: "#{choices_url}?method=#{choices_method}"
73
+ help: "#{choices_path}?method=#{choices_method}"
74
74
  }
75
75
  }
76
76
  end
@@ -68,7 +68,7 @@ module HaveAPI
68
68
  return @cache[:namespace] unless @cache[:namespace].nil?
69
69
  return @cache[:namespace] = @namespace unless @namespace.nil?
70
70
 
71
- n = @action.resource.to_s.demodulize.underscore
71
+ n = @action.resource.resource_name.underscore
72
72
  n = n.pluralize if %i(object_list hash_list).include?(layout)
73
73
  @cache[:namespace] = n.to_sym
74
74
  end
@@ -49,14 +49,20 @@ module HaveAPI
49
49
  end
50
50
 
51
51
  def self.resource_name
52
- ret = self.to_s.demodulize
52
+ (@resource_name ? @resource_name.to_s : to_s).demodulize
53
+ end
54
+
55
+ def self.resource_name=(name)
56
+ @resource_name = name
57
+ end
53
58
 
54
- singular ? ret.singularize.underscore : ret.tableize
59
+ def self.rest_name
60
+ singular ? resource_name.singularize.underscore : resource_name.tableize
55
61
  end
56
62
 
57
63
  def self.routes(prefix='/')
58
64
  ret = []
59
- prefix = "#{prefix}#{@route || resource_name}/"
65
+ prefix = "#{prefix}#{@route || rest_name}/"
60
66
 
61
67
  actions do |a|
62
68
  # Call used_by for selected model adapters. It is safe to do
@@ -78,29 +84,29 @@ module HaveAPI
78
84
 
79
85
  context.resource = self
80
86
 
81
- hash[:actions].each do |action, url|
87
+ hash[:actions].each do |action, path|
82
88
  context.action = action
83
- context.url = url
84
-
85
- a_name = action.to_s.demodulize.underscore
89
+ context.path = path
86
90
 
91
+ a_name = action.action_name.underscore
87
92
  a_desc = action.describe(context)
88
93
 
89
94
  ret[:actions][a_name] = a_desc if a_desc
90
95
  end
91
96
 
92
97
  hash[:resources].each do |resource, children|
93
- ret[:resources][resource.to_s.demodulize.underscore] = resource.describe(children, context)
98
+ ret[:resources][resource.resource_name.underscore] = resource.describe(children, context)
94
99
  end
95
100
 
96
101
  ret
97
102
  end
98
103
 
99
104
  def self.define_resource(name, superclass: Resource, &block)
100
- return false if const_defined?(name)
105
+ return false if const_defined?(name) && self != HaveAPI::Resource
101
106
 
102
107
  cls = Class.new(superclass)
103
- const_set(name, cls)
108
+ const_set(name, cls) if self != HaveAPI::Resource
109
+ cls.resource_name = name
104
110
  cls.class_exec(&block) if block
105
111
  cls
106
112
  end
@@ -110,6 +116,8 @@ module HaveAPI
110
116
 
111
117
  cls = Class.new(superclass)
112
118
  const_set(name, cls)
119
+ cls.resource = self
120
+ cls.action_name = name
113
121
  superclass.delayed_inherited(cls)
114
122
  cls.class_exec(&block)
115
123
  end
@@ -87,7 +87,7 @@ module HaveAPI::Resources
87
87
 
88
88
  desc 'Returns when the action is completed or timeout occurs'
89
89
  http_method :get
90
- route ':%{resource}_id/poll'
90
+ route '{%{resource}_id}/poll'
91
91
 
92
92
  input(:hash) do
93
93
  float :timeout, label: 'Timeout', desc: 'in seconds', default: 15, fill: true
@@ -159,7 +159,7 @@ module HaveAPI::Resources
159
159
 
160
160
  class Cancel < HaveAPI::Action
161
161
  http_method :post
162
- route ':%{resource}_id/cancel'
162
+ route '{%{resource}_id}/cancel'
163
163
  blocking true
164
164
 
165
165
  output(:hash) {}
data/lib/haveapi/route.rb CHANGED
@@ -1,9 +1,10 @@
1
1
  module HaveAPI
2
2
  class Route
3
- attr_reader :url, :action
3
+ attr_reader :path, :sinatra_path, :action
4
4
 
5
- def initialize(url, action)
6
- @url = url
5
+ def initialize(path, action)
6
+ @path = path
7
+ @sinatra_path = path.gsub(/:([a-zA-Z\-_]+)/, '{\1}')
7
8
  @action = action
8
9
  end
9
10
 
@@ -387,7 +387,7 @@ module HaveAPI
387
387
  )
388
388
 
389
389
  else
390
- hash[resource][:actions][route.action] = route.url
390
+ hash[resource][:actions][route.action] = route.path
391
391
  mount_action(v, route)
392
392
  end
393
393
  end
@@ -401,7 +401,7 @@ module HaveAPI
401
401
  ret[:resources][route.keys.first] = mount_nested_resource(v, route.values.first)
402
402
 
403
403
  else
404
- ret[:actions][route.action] = route.url
404
+ ret[:actions][route.action] = route.path
405
405
  mount_action(v, route)
406
406
  end
407
407
  end
@@ -410,7 +410,7 @@ module HaveAPI
410
410
  end
411
411
 
412
412
  def mount_action(v, route)
413
- @sinatra.method(route.http_method).call(route.url) do
413
+ @sinatra.method(route.http_method).call(route.sinatra_path) do
414
414
  if route.action.auth
415
415
  authenticate!(v)
416
416
  else
@@ -437,7 +437,7 @@ module HaveAPI
437
437
  version: v,
438
438
  request: self,
439
439
  action: route.action,
440
- url: route.url,
440
+ path: route.path,
441
441
  params: params,
442
442
  user: current_user,
443
443
  endpoint: true
@@ -462,7 +462,7 @@ module HaveAPI
462
462
  ]
463
463
  end
464
464
 
465
- @sinatra.options route.url do |*args|
465
+ @sinatra.options route.sinatra_path do |*args|
466
466
  access_control
467
467
  route_method = route.http_method.to_s.upcase
468
468
 
@@ -479,7 +479,7 @@ module HaveAPI
479
479
  version: v,
480
480
  request: self,
481
481
  action: route.action,
482
- url: route.url,
482
+ path: route.path,
483
483
  args: args,
484
484
  params: params,
485
485
  user: current_user,
@@ -533,7 +533,7 @@ module HaveAPI
533
533
  #puts JSON.pretty_generate(@routes)
534
534
 
535
535
  @routes[context.version][:resources].each do |resource, children|
536
- r_name = resource.to_s.demodulize.underscore
536
+ r_name = resource.resource_name.underscore
537
537
  r_desc = describe_resource(resource, children, context)
538
538
 
539
539
  unless r_desc[:actions].empty? && r_desc[:resources].empty?
@@ -1,10 +1,10 @@
1
1
  module HaveAPI::Spec
2
2
  class MockAction
3
- def initialize(test, server, action, url, v)
3
+ def initialize(test, server, action, path, v)
4
4
  @test = test
5
5
  @server = server
6
6
  @action = action
7
- @url = url
7
+ @path = path
8
8
  @v = v
9
9
  end
10
10
 
@@ -13,7 +13,7 @@ module HaveAPI::Spec
13
13
  @server,
14
14
  version: @v,
15
15
  action: @action,
16
- url: @url,
16
+ path: @path,
17
17
  params: input,
18
18
  user: user,
19
19
  endpoint: true
@@ -26,7 +26,7 @@ module HaveAPI::Spec
26
26
  # This method is a wrapper for Rack::Test::Methods. Input parameters
27
27
  # are encoded into JSON and sent with a correct Content-Type.
28
28
  # Two modes:
29
- # http_method, url, params = {}
29
+ # http_method, path, params = {}
30
30
  # [resource], action, params, &block
31
31
  def call_api(*args, &block)
32
32
  if args[0].is_a?(::Array) || args[1].is_a?(::Symbol)
@@ -34,22 +34,22 @@ module HaveAPI::Spec
34
34
 
35
35
  app
36
36
 
37
- action, url = find_action(
37
+ action, path = find_action(
38
38
  (params && params[:version]) || @api.default_version,
39
39
  r_name, a_name
40
40
  )
41
41
 
42
42
  method(action.http_method).call(
43
- url,
43
+ path,
44
44
  params && params.to_json,
45
45
  {'Content-Type' => 'application/json'}
46
46
  )
47
47
 
48
48
  else
49
- http_method, url, params = args
49
+ http_method, path, params = args
50
50
 
51
51
  method(http_method).call(
52
- url,
52
+ path,
53
53
  params && params.to_json,
54
54
  {'Content-Type' => 'application/json'}
55
55
  )
@@ -77,8 +77,8 @@ module HaveAPI::Spec
77
77
  def mock_action(r_name, a_name, params, version: nil, user: nil, &block)
78
78
  app
79
79
  v = version || @api.default_version
80
- action, url = find_action(v, r_name, a_name)
81
- m = MockAction.new(self, @api, action, url, v)
80
+ action, path = find_action(v, r_name, a_name)
81
+ m = MockAction.new(self, @api, action, path, v)
82
82
  m.call(params, user: user, &block)
83
83
  end
84
84
 
@@ -106,7 +106,7 @@ module HaveAPI::Spec
106
106
  resources = r_name.is_a?(::Array) ? r_name : [r_name]
107
107
 
108
108
  top = @api.routes[v]
109
-
109
+
110
110
  resources.each do |r|
111
111
  top = top[:resources].detect do |k, _|
112
112
  k.resource_name.to_sym == r
@@ -1,4 +1,4 @@
1
1
  module HaveAPI
2
- PROTOCOL_VERSION = '1.2'
3
- VERSION = '0.12.1'
2
+ PROTOCOL_VERSION = '2.0'
3
+ VERSION = '0.13.0'
4
4
  end
@@ -86,8 +86,8 @@ end
86
86
  <h3 id="<%= "#{prefix}-#{resource}-#{action}" %>"><%= name %> # <%= action.capitalize %></h3>
87
87
  <div class="action">
88
88
  <dl>
89
- <dt>URL:</dt>
90
- <dd><%= info[:method] %> <%= info[:url] %></dd>
89
+ <dt>Path:</dt>
90
+ <dd><%= info[:method] %> <%= info[:path] %></dd>
91
91
  <dt>Description:</dt>
92
92
  <dd><%= info[:description] %></dd>
93
93
  <dt>Authentication required:</dt>
data/shell.nix CHANGED
@@ -13,7 +13,7 @@ in stdenv.mkDerivation rec {
13
13
 
14
14
  shellHook = ''
15
15
  export GEM_HOME=$(pwd)/../../.gems
16
- export PATH="$GEM_HOME/.gems/bin:$PATH"
16
+ export PATH="$GEM_HOME/bin:$PATH"
17
17
  gem install bundler
18
18
  bundle install
19
19
  '';
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haveapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.1
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakub Skokan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-27 00:00:00.000000000 Z
11
+ date: 2019-05-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: require_all
@@ -142,14 +142,14 @@ dependencies:
142
142
  requirements:
143
143
  - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: 0.12.0
145
+ version: 0.13.0
146
146
  type: :runtime
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
- version: 0.12.0
152
+ version: 0.13.0
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: mail
155
155
  requirement: !ruby/object:Gem::Requirement
@@ -193,8 +193,12 @@ files:
193
193
  - lib/haveapi/authentication/base.rb
194
194
  - lib/haveapi/authentication/basic/provider.rb
195
195
  - lib/haveapi/authentication/chain.rb
196
+ - lib/haveapi/authentication/token.rb
197
+ - lib/haveapi/authentication/token/action_config.rb
198
+ - lib/haveapi/authentication/token/action_request.rb
199
+ - lib/haveapi/authentication/token/action_result.rb
200
+ - lib/haveapi/authentication/token/config.rb
196
201
  - lib/haveapi/authentication/token/provider.rb
197
- - lib/haveapi/authentication/token/resources.rb
198
202
  - lib/haveapi/authorization.rb
199
203
  - lib/haveapi/client_example.rb
200
204
  - lib/haveapi/client_examples/curl.rb
@@ -1,110 +0,0 @@
1
- require 'haveapi/resource'
2
- require 'haveapi/action'
3
-
4
- module HaveAPI::Authentication::Token
5
- module Resources
6
- class Token < HaveAPI::Resource
7
- auth false
8
- version :all
9
-
10
- class << self
11
- attr_accessor :token_instance
12
- end
13
-
14
- class Request < HaveAPI::Action
15
- route ''
16
- http_method :post
17
-
18
- input(:hash) do
19
- string :login, label: 'Login', required: true
20
- password :password, label: 'Password', required: true
21
- string :lifetime, label: 'Lifetime', required: true,
22
- choices: %i(fixed renewable_manual renewable_auto permanent),
23
- desc: <<END
24
- fixed - the token has a fixed validity period, it cannot be renewed
25
- renewable_manual - the token can be renewed, but it must be done manually via renew action
26
- renewable_auto - the token is renewed automatically to now+interval every time it is used
27
- permanent - the token will be valid forever, unless deleted
28
- END
29
- integer :interval, label: 'Interval',
30
- desc: 'How long will requested token be valid, in seconds.',
31
- default: 60*5, fill: true
32
- end
33
-
34
- output(:hash) do
35
- string :token
36
- datetime :valid_to
37
- end
38
-
39
- authorize do
40
- allow
41
- end
42
-
43
- def exec
44
- klass = self.class.resource.token_instance[@version]
45
-
46
- user = klass.send(
47
- :find_user_by_credentials,
48
- request,
49
- input[:login],
50
- input[:password]
51
- )
52
- error('bad login or password') unless user
53
-
54
- token = expiration = nil
55
-
56
- loop do
57
- begin
58
- token = klass.send(:generate_token)
59
- expiration = klass.send(:save_token,
60
- @request,
61
- user,
62
- token,
63
- params[:token][:lifetime],
64
- params[:token][:interval])
65
- break
66
-
67
- rescue TokenExists
68
- next
69
- end
70
- end
71
-
72
- {token: token, valid_to: expiration}
73
- end
74
- end
75
-
76
- class Revoke < HaveAPI::Action
77
- # route ''
78
- http_method :post
79
- auth true
80
-
81
- authorize do
82
- allow
83
- end
84
-
85
- def exec
86
- klass = self.class.resource.token_instance[@version]
87
- klass.send(:revoke_token, request, current_user, klass.token(request))
88
- end
89
- end
90
-
91
- class Renew < HaveAPI::Action
92
- http_method :post
93
- auth true
94
-
95
- output(:hash) do
96
- datetime :valid_to
97
- end
98
-
99
- authorize do
100
- allow
101
- end
102
-
103
- def exec
104
- klass = self.class.resource.token_instance[@version]
105
- {valid_to: klass.send(:renew_token, request, current_user, klass.token(request))}
106
- end
107
- end
108
- end
109
- end
110
- end