haveapi 0.12.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
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