haveapi-client 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/README.md +67 -0
- data/Rakefile +2 -0
- data/bin/haveapi-cli +5 -0
- data/haveapi-client.gemspec +30 -0
- data/lib/haveapi/cli.rb +2 -0
- data/lib/haveapi/cli/cli.rb +290 -0
- data/lib/haveapi/client.rb +2 -0
- data/lib/haveapi/client/action.rb +81 -0
- data/lib/haveapi/client/client.rb +37 -0
- data/lib/haveapi/client/communicator.rb +115 -0
- data/lib/haveapi/client/exceptions.rb +13 -0
- data/lib/haveapi/client/inflections.rb +3 -0
- data/lib/haveapi/client/resource.rb +78 -0
- data/lib/haveapi/client/response.rb +33 -0
- data/lib/haveapi/client/version.rb +5 -0
- data/lib/restclient_ext/request.rb +16 -0
- data/lib/restclient_ext/resource.rb +11 -0
- metadata +175 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 411ee5aebf1042dcb0532fcfde75bb90a71a6a99
|
4
|
+
data.tar.gz: 81323b4e42c302b2030b051308082dca268556a1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8fde9de9cdd152dca98d07b1f6fd74dbeb599c202a3031df0158b803e213047ee648b38077f8270c86d8b0bc0272a03e57c57a3a06354f5a0b44556c582ba613
|
7
|
+
data.tar.gz: 4265a74df48c07c9dc3ae1a706f833cc2be1a9eb7be40fa54abedeb00808e1e588a98a1543d8da318c73dfe007dbf1bdd6d71f5d9dc64e46c806a67702ef8d5d
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
HaveAPI-Client
|
2
|
+
--------------
|
3
|
+
HaveAPI-Client is a Ruby CLI and client library for APIs built with
|
4
|
+
[HaveAPI framework](https://github.com/vpsfreecz/haveapi).
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'haveapi-client'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install haveapi-client
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
### CLI
|
22
|
+
$ haveapi-cli -h
|
23
|
+
Usage: haveapi-cli [options] <resource> <action> [objects ids] [-- [parameters]]
|
24
|
+
-a, --api URL API URL
|
25
|
+
--list-versions List all available API versions
|
26
|
+
--list-resources VERSION List all resource in API version
|
27
|
+
--list-actions VERSION List all resources and actions in API version
|
28
|
+
-r, --raw Print raw response as is
|
29
|
+
-u, --username USER User name
|
30
|
+
-p, --password PASSWORD Password
|
31
|
+
-v, --[no-]verbose Run verbosely
|
32
|
+
-h, --help Show this message
|
33
|
+
|
34
|
+
Using the API example from
|
35
|
+
[HaveAPI README](https://github.com/vpsfreecz/haveapi/blob/master/README.md#example),
|
36
|
+
users would be listed with:
|
37
|
+
|
38
|
+
$ haveapi-cli -a https://your.api.tld user index -u yourname -p yourpassword
|
39
|
+
|
40
|
+
Nested resources and object IDs:
|
41
|
+
|
42
|
+
$ haveapi-cli -a https://your.api.tld user.invoice index 10 -u yourname -p yourpassword
|
43
|
+
|
44
|
+
where `10` is user ID.
|
45
|
+
|
46
|
+
### Client library
|
47
|
+
```ruby
|
48
|
+
require 'haveapi/client'
|
49
|
+
|
50
|
+
api = HaveAPI::Client::Client.new('https://your.api.tld')
|
51
|
+
api.login('yourname', 'yourpassword')
|
52
|
+
|
53
|
+
response = api.user.index
|
54
|
+
p response.ok?
|
55
|
+
p response.response
|
56
|
+
|
57
|
+
p api.user(10).invoice
|
58
|
+
p api.user(10).delete
|
59
|
+
p api.user(10).delete!
|
60
|
+
p api.user.delete(10)
|
61
|
+
|
62
|
+
p api.user.create({
|
63
|
+
login: 'mylogin',
|
64
|
+
full_name: 'Very Full Name',
|
65
|
+
role: 'user'
|
66
|
+
})
|
67
|
+
```
|
data/Rakefile
ADDED
data/bin/haveapi-cli
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'haveapi/client/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'haveapi-client'
|
8
|
+
spec.version = HaveAPI::Client::VERSION
|
9
|
+
spec.authors = ['Jakub Skokan']
|
10
|
+
spec.email = ['jakub.skokan@vpsfree.cz']
|
11
|
+
spec.summary =
|
12
|
+
spec.description = 'Ruby API and CLI for HaveAPI'
|
13
|
+
spec.homepage = ''
|
14
|
+
spec.license = 'GPL'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
|
24
|
+
spec.add_runtime_dependency 'activesupport', '~> 4.1.0'
|
25
|
+
spec.add_runtime_dependency 'require_all', '~> 1.3.0'
|
26
|
+
spec.add_runtime_dependency 'rest_client', '~> 1.7.3'
|
27
|
+
spec.add_runtime_dependency 'json', '~> 1.8.1'
|
28
|
+
spec.add_runtime_dependency 'highline', '~> 1.6.21'
|
29
|
+
spec.add_runtime_dependency 'table_print', '~> 1.5.1'
|
30
|
+
end
|
data/lib/haveapi/cli.rb
ADDED
@@ -0,0 +1,290 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'pp'
|
3
|
+
require 'highline/import'
|
4
|
+
require 'table_print'
|
5
|
+
|
6
|
+
module HaveAPI
|
7
|
+
module CLI
|
8
|
+
class Cli
|
9
|
+
def self.run
|
10
|
+
c = new
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
args, @opts = options
|
15
|
+
@api = HaveAPI::Client::Communicator.new(api_url)
|
16
|
+
|
17
|
+
if @action
|
18
|
+
method(@action.first).call( * @action[1..-1] )
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
|
22
|
+
if @opts[:help] || args.empty?
|
23
|
+
puts @global_opt.help
|
24
|
+
exit(true)
|
25
|
+
end
|
26
|
+
|
27
|
+
resources = args[0].split('.')
|
28
|
+
action = translate_action(args[1].to_sym)
|
29
|
+
|
30
|
+
action = @api.get_action(resources, action, args[2..-1])
|
31
|
+
|
32
|
+
@input_params = parameters(action)
|
33
|
+
|
34
|
+
if action
|
35
|
+
unless params_valid?(action)
|
36
|
+
warn 'Missing required parameters'
|
37
|
+
end
|
38
|
+
|
39
|
+
ret = action.execute(@input_params, raw: @opts[:raw])
|
40
|
+
|
41
|
+
if ret[:status]
|
42
|
+
format_output(action, ret[:response])
|
43
|
+
else
|
44
|
+
warn "Action failed: #{ret[:message]}"
|
45
|
+
|
46
|
+
if ret[:errors].any?
|
47
|
+
puts 'Errors:'
|
48
|
+
ret[:errors].each do |param, e|
|
49
|
+
puts "\t#{param}: #{e.join('; ')}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
else
|
55
|
+
warn "Action #{ARGV[0]}##{ARGV[1]} not valid"
|
56
|
+
exit(false)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def api_url
|
61
|
+
@opts[:client]
|
62
|
+
end
|
63
|
+
|
64
|
+
def options
|
65
|
+
options = {
|
66
|
+
client: default_url,
|
67
|
+
verbose: false,
|
68
|
+
}
|
69
|
+
|
70
|
+
@global_opt = OptionParser.new do |opts|
|
71
|
+
opts.banner = "Usage: #{$0} [options] <resource> <action> [objects ids] [-- [parameters]]"
|
72
|
+
|
73
|
+
opts.on('-a', '--api URL', 'API URL') do |url|
|
74
|
+
options[:client] = url
|
75
|
+
end
|
76
|
+
|
77
|
+
opts.on('--list-versions', 'List all available API versions') do
|
78
|
+
@action = [:list_versions]
|
79
|
+
end
|
80
|
+
|
81
|
+
opts.on('--list-resources VERSION', 'List all resource in API version') do |v|
|
82
|
+
@action = [:list_resources, v.sub(/^v/, '')]
|
83
|
+
end
|
84
|
+
|
85
|
+
opts.on('--list-actions VERSION', 'List all resources and actions in API version') do |v|
|
86
|
+
@action = [:list_actions, v.sub(/^v/, '')]
|
87
|
+
end
|
88
|
+
|
89
|
+
opts.on('-r', '--raw', 'Print raw response as is') do
|
90
|
+
options[:raw] = true
|
91
|
+
end
|
92
|
+
|
93
|
+
opts.on('-u', '--username USER', 'User name') do |u|
|
94
|
+
options[:user] = u
|
95
|
+
end
|
96
|
+
|
97
|
+
opts.on('-p', '--password PASSWORD', 'Password') do |p|
|
98
|
+
options[:password] = p
|
99
|
+
end
|
100
|
+
|
101
|
+
opts.on('-v', '--[no-]verbose', 'Run verbosely') do |v|
|
102
|
+
options[:verbose] = v
|
103
|
+
end
|
104
|
+
|
105
|
+
opts.on('-h', '--help', 'Show this message') do
|
106
|
+
options[:help] = true
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
args = []
|
111
|
+
|
112
|
+
ARGV.each do |arg|
|
113
|
+
if arg == '--'
|
114
|
+
break
|
115
|
+
else
|
116
|
+
args << arg
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
@global_opt.parse!(args)
|
121
|
+
|
122
|
+
# p options
|
123
|
+
#p ARGV
|
124
|
+
|
125
|
+
[args, options]
|
126
|
+
end
|
127
|
+
|
128
|
+
def parameters(action)
|
129
|
+
options = {}
|
130
|
+
sep = ARGV.index('--')
|
131
|
+
|
132
|
+
@action_opt = OptionParser.new do |opts|
|
133
|
+
opts.banner = ''
|
134
|
+
|
135
|
+
action.input[:parameters].each do |name, p|
|
136
|
+
opts.on(param_option(name, p), p[:description] || p[:label] || '') do |*args|
|
137
|
+
options[name] = args.first
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
opts.on('-h', '--help', 'Show this message') do
|
142
|
+
@opts[:help] = true
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
if @opts[:help]
|
147
|
+
puts @global_opt.help
|
148
|
+
puts ''
|
149
|
+
print 'Action parameters:'
|
150
|
+
puts @action_opt.help
|
151
|
+
exit
|
152
|
+
end
|
153
|
+
|
154
|
+
return {} unless sep
|
155
|
+
|
156
|
+
@action_opt.parse!(ARGV[sep+1..-1])
|
157
|
+
|
158
|
+
options
|
159
|
+
end
|
160
|
+
|
161
|
+
def param_option(name, p)
|
162
|
+
ret = '--'
|
163
|
+
name = name.to_s.dasherize
|
164
|
+
|
165
|
+
if p[:type] == 'Boolean'
|
166
|
+
ret += "[no-]#{name}"
|
167
|
+
|
168
|
+
else
|
169
|
+
ret += "#{name} #{name.underscore.upcase}"
|
170
|
+
end
|
171
|
+
|
172
|
+
ret
|
173
|
+
end
|
174
|
+
|
175
|
+
def translate_action(action)
|
176
|
+
tr = {
|
177
|
+
list: :index,
|
178
|
+
new: :create,
|
179
|
+
change: :update
|
180
|
+
}
|
181
|
+
|
182
|
+
if tr.has_key?(action)
|
183
|
+
return tr[action]
|
184
|
+
end
|
185
|
+
|
186
|
+
action
|
187
|
+
end
|
188
|
+
|
189
|
+
def list_versions
|
190
|
+
desc = @api.describe_api
|
191
|
+
|
192
|
+
desc[:versions].each do |v, _|
|
193
|
+
next if v == :default
|
194
|
+
|
195
|
+
v_int = v.to_s.to_i
|
196
|
+
|
197
|
+
puts "#{v_int == desc[:default_version] ? '*' : ' '} v#{v}"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def list_resources(v)
|
202
|
+
@api.describe_api(v)[:resources].each do |resource, children|
|
203
|
+
nested_resource(resource, children, false)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def list_actions(v)
|
208
|
+
@api.describe_api(v)[:resources].each do |resource, children|
|
209
|
+
nested_resource(resource, children, true)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def nested_resource(prefix, children, actions=false)
|
214
|
+
if actions
|
215
|
+
children[:actions].each do |action, _|
|
216
|
+
puts "#{prefix}##{action}"
|
217
|
+
end
|
218
|
+
else
|
219
|
+
puts prefix
|
220
|
+
end
|
221
|
+
|
222
|
+
children[:resources].each do |resource, children|
|
223
|
+
nested_resource("#{prefix}.#{resource}", children, actions)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def format_output(action, response)
|
228
|
+
if @opts[:raw]
|
229
|
+
puts response
|
230
|
+
return
|
231
|
+
end
|
232
|
+
|
233
|
+
return if response.empty?
|
234
|
+
|
235
|
+
s = action.structure
|
236
|
+
namespace = action.namespace(:output).to_sym
|
237
|
+
|
238
|
+
case action.layout.to_sym
|
239
|
+
when :list
|
240
|
+
tp response[namespace]
|
241
|
+
|
242
|
+
|
243
|
+
when :object
|
244
|
+
response[namespace].each do |k, v|
|
245
|
+
puts "#{k}: #{v}"
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
when :custom
|
250
|
+
pp response[namespace]
|
251
|
+
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def header_for(action, param)
|
256
|
+
params = action.params
|
257
|
+
|
258
|
+
if params.has_key?(param) && params[param][:label]
|
259
|
+
params[param][:label]
|
260
|
+
else
|
261
|
+
param.to_s.upcase
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def params_valid?(action)
|
266
|
+
if action.auth?
|
267
|
+
@opts[:user] ||= ask('User name: ') { |q| q.default = nil }
|
268
|
+
|
269
|
+
@opts[:password] ||= ask('Password: ') do |q|
|
270
|
+
q.default = nil
|
271
|
+
q.echo = false
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
if action.auth? && !(@opts[:user] || @opts[:password])
|
276
|
+
return false
|
277
|
+
end
|
278
|
+
|
279
|
+
@api.login(@opts[:user], @opts[:password])
|
280
|
+
|
281
|
+
true
|
282
|
+
end
|
283
|
+
|
284
|
+
protected
|
285
|
+
def default_url
|
286
|
+
'http://localhost:4567'
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module HaveAPI
|
2
|
+
module Client
|
3
|
+
class Action
|
4
|
+
def initialize(api, name, spec, args)
|
5
|
+
@api = api
|
6
|
+
@name = name
|
7
|
+
@spec = spec
|
8
|
+
|
9
|
+
apply_args(args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(*args)
|
13
|
+
ret = @api.call(self, *args)
|
14
|
+
@prepared_url = nil
|
15
|
+
ret
|
16
|
+
end
|
17
|
+
|
18
|
+
def name
|
19
|
+
@name
|
20
|
+
end
|
21
|
+
|
22
|
+
def auth?
|
23
|
+
@spec[:auth]
|
24
|
+
end
|
25
|
+
|
26
|
+
def input
|
27
|
+
@spec[:input]
|
28
|
+
end
|
29
|
+
|
30
|
+
def output
|
31
|
+
@spec[:output]
|
32
|
+
end
|
33
|
+
|
34
|
+
def layout
|
35
|
+
@spec[:output][:layout]
|
36
|
+
end
|
37
|
+
|
38
|
+
def structure
|
39
|
+
@spec[:output][:format]
|
40
|
+
end
|
41
|
+
|
42
|
+
def namespace(src)
|
43
|
+
@spec[src][:namespace]
|
44
|
+
end
|
45
|
+
|
46
|
+
def params
|
47
|
+
@spec[:output][:parameters]
|
48
|
+
end
|
49
|
+
|
50
|
+
def url
|
51
|
+
@spec[:url]
|
52
|
+
end
|
53
|
+
|
54
|
+
# Url with resolved parameters.
|
55
|
+
def prepared_url
|
56
|
+
@prepared_url || @spec[:url]
|
57
|
+
end
|
58
|
+
|
59
|
+
def http_method
|
60
|
+
@spec[:method]
|
61
|
+
end
|
62
|
+
|
63
|
+
def unresolved_args?
|
64
|
+
prepared_url =~ /:[a-zA-Z\-_]+/
|
65
|
+
end
|
66
|
+
|
67
|
+
def provide_args(*args)
|
68
|
+
apply_args(args)
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
def apply_args(args)
|
73
|
+
@prepared_url ||= @spec[:url].dup
|
74
|
+
|
75
|
+
args.each do |arg|
|
76
|
+
@prepared_url.sub!(/:[a-zA-Z\-_]+/, arg.to_s)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'pp'
|
2
|
+
|
3
|
+
class HaveAPI::Client::Client
|
4
|
+
attr_reader :resources
|
5
|
+
|
6
|
+
def initialize(url, v=nil)
|
7
|
+
@version = v
|
8
|
+
@api = VpsAdmin::API::Communicator.new(url)
|
9
|
+
|
10
|
+
setup_api(@api.describe_api)
|
11
|
+
end
|
12
|
+
|
13
|
+
def login(*credentials)
|
14
|
+
@api.login(*credentials)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def setup_api(description)
|
19
|
+
v = @version || description[:default_version]
|
20
|
+
|
21
|
+
@resources = {}
|
22
|
+
|
23
|
+
description[:versions][v.to_s.to_sym][:resources].each do |name, desc|
|
24
|
+
r = VpsAdmin::API::Resource.new(@api, name)
|
25
|
+
r.setup(desc)
|
26
|
+
|
27
|
+
define_singleton_method(name) do |*args|
|
28
|
+
tmp = r.dup
|
29
|
+
tmp.prepared_args = args
|
30
|
+
tmp.setup_from_clone(r)
|
31
|
+
tmp
|
32
|
+
end
|
33
|
+
|
34
|
+
@resources[name] = r
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
require 'json'
|
3
|
+
require 'active_support/inflections'
|
4
|
+
require 'active_support/inflector'
|
5
|
+
require_rel '../../restclient_ext'
|
6
|
+
|
7
|
+
module HaveAPI
|
8
|
+
module Client
|
9
|
+
class Communicator
|
10
|
+
def initialize(url)
|
11
|
+
@url = url
|
12
|
+
@rest = RestClient::Resource.new(@url)
|
13
|
+
@version = 1
|
14
|
+
end
|
15
|
+
|
16
|
+
def login(user, password)
|
17
|
+
@rest = RestClient::Resource.new(@url, user, password)
|
18
|
+
end
|
19
|
+
|
20
|
+
def describe_api(v=nil)
|
21
|
+
description_for(path_for(v))
|
22
|
+
end
|
23
|
+
|
24
|
+
def describe_action(v, r)
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_action(resources, action, args)
|
29
|
+
@spec ||= describe_api(@version)
|
30
|
+
|
31
|
+
tmp = @spec
|
32
|
+
|
33
|
+
resources.each do |r|
|
34
|
+
tmp = tmp[:resources][r.to_sym]
|
35
|
+
|
36
|
+
return false unless tmp
|
37
|
+
end
|
38
|
+
|
39
|
+
a = tmp[:actions][action]
|
40
|
+
|
41
|
+
if a
|
42
|
+
Action.new(self, action, a, args)
|
43
|
+
else
|
44
|
+
false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def call(action, params, raw: false)
|
49
|
+
args = []
|
50
|
+
input_namespace = action.namespace(:input)
|
51
|
+
|
52
|
+
if %w(POST PUT).include?(action.http_method)
|
53
|
+
args << {input_namespace => params}.to_json
|
54
|
+
args << {:content_type => :json, :accept => :json}
|
55
|
+
|
56
|
+
elsif %w(GET DELETE).include?(action.http_method)
|
57
|
+
get_params = {}
|
58
|
+
|
59
|
+
params.each do |k, v|
|
60
|
+
get_params["#{input_namespace}[#{k}]"] = v
|
61
|
+
end
|
62
|
+
|
63
|
+
args << {params: get_params, accept: :json}
|
64
|
+
end
|
65
|
+
|
66
|
+
begin
|
67
|
+
response = parse(@rest[action.prepared_url].method(action.http_method.downcase.to_sym).call(*args))
|
68
|
+
|
69
|
+
rescue RestClient::Forbidden
|
70
|
+
return error('Access forbidden. Bad user name or password? Not authorized?')
|
71
|
+
|
72
|
+
rescue => e
|
73
|
+
return error("Fatal API error: #{e.inspect}")
|
74
|
+
end
|
75
|
+
|
76
|
+
if response[:status]
|
77
|
+
if raw
|
78
|
+
ok(JSON.pretty_generate(response[:response]))
|
79
|
+
else
|
80
|
+
ok(response[:response])
|
81
|
+
end
|
82
|
+
|
83
|
+
else
|
84
|
+
error(response[:message], response[:errors])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
def ok(response)
|
90
|
+
{status: true, response: response}
|
91
|
+
end
|
92
|
+
|
93
|
+
def error(msg, errors={})
|
94
|
+
{status: false, message: msg, errors: errors}
|
95
|
+
end
|
96
|
+
|
97
|
+
def path_for(v=nil, r=nil)
|
98
|
+
ret = '/'
|
99
|
+
|
100
|
+
ret += "v#{v}/" if v
|
101
|
+
ret += r if r
|
102
|
+
|
103
|
+
ret
|
104
|
+
end
|
105
|
+
|
106
|
+
def description_for(path)
|
107
|
+
parse(@rest[path].get_options)
|
108
|
+
end
|
109
|
+
|
110
|
+
def parse(str)
|
111
|
+
JSON.parse(str, symbolize_names: true)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class HaveAPI::Client::Resource
|
2
|
+
attr_reader :actions, :resources, :name
|
3
|
+
attr_accessor :prepared_args
|
4
|
+
|
5
|
+
def initialize(api, name)
|
6
|
+
@api = api
|
7
|
+
@name = name
|
8
|
+
@prepared_args = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def setup(description)
|
12
|
+
@actions = {}
|
13
|
+
@resources = {}
|
14
|
+
|
15
|
+
description[:actions].each do |name, desc|
|
16
|
+
action = HaveAPI::Client::Action.new(@api, name, desc, [])
|
17
|
+
define_action(action)
|
18
|
+
@actions[name] = action
|
19
|
+
end
|
20
|
+
|
21
|
+
description[:resources].each do |name, desc|
|
22
|
+
r = HaveAPI::Client::Resource.new(@api, name)
|
23
|
+
r.setup(desc)
|
24
|
+
define_resource(r)
|
25
|
+
@resources[name] = r
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def setup_from_clone(original)
|
30
|
+
original.actions.each_value do |action|
|
31
|
+
define_action(action)
|
32
|
+
end
|
33
|
+
|
34
|
+
original.resources.each_value do |resource|
|
35
|
+
define_resource(resource)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def define_action(action)
|
41
|
+
define_singleton_method(action.name) do |*args|
|
42
|
+
all_args = @prepared_args + args
|
43
|
+
|
44
|
+
if action.unresolved_args?
|
45
|
+
all_args.delete_if do |arg|
|
46
|
+
break unless action.unresolved_args?
|
47
|
+
|
48
|
+
action.provide_args(arg)
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
if action.unresolved_args?
|
53
|
+
raise ArgumentError.new('One or more object ids missing')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
all_args << {} if all_args.empty?
|
58
|
+
|
59
|
+
HaveAPI::Client::Response.new(action, action.execute(*all_args))
|
60
|
+
end
|
61
|
+
|
62
|
+
define_singleton_method("#{action.name}!".to_sym) do |*args|
|
63
|
+
ret = method(action.name).call(*args)
|
64
|
+
raise HaveAPI::Client::ActionFailed.new(ret) unless ret.ok?
|
65
|
+
|
66
|
+
ret
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def define_resource(resource)
|
71
|
+
define_singleton_method(resource.name) do |*args|
|
72
|
+
tmp = resource.dup
|
73
|
+
tmp.prepared_args = @prepared_args + args
|
74
|
+
tmp.setup_from_clone(resource)
|
75
|
+
tmp
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class HaveAPI::Client::Response
|
2
|
+
attr_reader :action
|
3
|
+
|
4
|
+
def initialize(action, response)
|
5
|
+
@action = action
|
6
|
+
@response = response
|
7
|
+
end
|
8
|
+
|
9
|
+
def ok?
|
10
|
+
@response[:status]
|
11
|
+
end
|
12
|
+
|
13
|
+
def failed?
|
14
|
+
!ok?
|
15
|
+
end
|
16
|
+
|
17
|
+
def response
|
18
|
+
case @action.layout.to_sym
|
19
|
+
when :object, :list
|
20
|
+
@response[:response][@action.namespace(:output).to_sym]
|
21
|
+
else
|
22
|
+
@response[:response]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def message
|
27
|
+
@response[:message]
|
28
|
+
end
|
29
|
+
|
30
|
+
def errors
|
31
|
+
@response[:errors]
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module RestClient
|
2
|
+
class Request
|
3
|
+
def self.execute_options(args, &block)
|
4
|
+
new(args).execute_options(&block)
|
5
|
+
end
|
6
|
+
|
7
|
+
def execute_options(&block)
|
8
|
+
uri = parse_url_with_auth(url)
|
9
|
+
http_request = Net::HTTPGenericRequest.new(method.to_s.upcase, false, true, uri.request_uri, processed_headers)
|
10
|
+
|
11
|
+
transmit uri, http_request, payload, & block
|
12
|
+
ensure
|
13
|
+
payload.close if payload
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module RestClient
|
2
|
+
class Resource
|
3
|
+
def get_options(additional_headers={}, &block)
|
4
|
+
headers = (options[:headers] || {}).merge(additional_headers)
|
5
|
+
Request.execute_options(options.merge(
|
6
|
+
:method => :options,
|
7
|
+
:url => url,
|
8
|
+
:headers => headers), &(block || @block))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
metadata
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: haveapi-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jakub Skokan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-05-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activesupport
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 4.1.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 4.1.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: require_all
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.3.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.3.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rest_client
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.7.3
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.7.3
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: json
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.8.1
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.8.1
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: highline
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 1.6.21
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.6.21
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: table_print
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.5.1
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ~>
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 1.5.1
|
125
|
+
description: Ruby API and CLI for HaveAPI
|
126
|
+
email:
|
127
|
+
- jakub.skokan@vpsfree.cz
|
128
|
+
executables:
|
129
|
+
- haveapi-cli
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- Gemfile
|
134
|
+
- README.md
|
135
|
+
- Rakefile
|
136
|
+
- bin/haveapi-cli
|
137
|
+
- haveapi-client.gemspec
|
138
|
+
- lib/haveapi/cli.rb
|
139
|
+
- lib/haveapi/cli/cli.rb
|
140
|
+
- lib/haveapi/client.rb
|
141
|
+
- lib/haveapi/client/action.rb
|
142
|
+
- lib/haveapi/client/client.rb
|
143
|
+
- lib/haveapi/client/communicator.rb
|
144
|
+
- lib/haveapi/client/exceptions.rb
|
145
|
+
- lib/haveapi/client/inflections.rb
|
146
|
+
- lib/haveapi/client/resource.rb
|
147
|
+
- lib/haveapi/client/response.rb
|
148
|
+
- lib/haveapi/client/version.rb
|
149
|
+
- lib/restclient_ext/request.rb
|
150
|
+
- lib/restclient_ext/resource.rb
|
151
|
+
homepage: ''
|
152
|
+
licenses:
|
153
|
+
- GPL
|
154
|
+
metadata: {}
|
155
|
+
post_install_message:
|
156
|
+
rdoc_options: []
|
157
|
+
require_paths:
|
158
|
+
- lib
|
159
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - '>='
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: '0'
|
164
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
|
+
requirements:
|
166
|
+
- - '>='
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: '0'
|
169
|
+
requirements: []
|
170
|
+
rubyforge_project:
|
171
|
+
rubygems_version: 2.1.11
|
172
|
+
signing_key:
|
173
|
+
specification_version: 4
|
174
|
+
summary: Ruby API and CLI for HaveAPI
|
175
|
+
test_files: []
|