heroics 0.0.12 → 0.0.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -0
- data/bin/heroics-generate +4 -3
- data/lib/heroics/link.rb +4 -4
- data/lib/heroics/schema.rb +20 -3
- data/lib/heroics/version.rb +1 -1
- data/test/helper.rb +19 -0
- data/test/link_test.rb +53 -4
- data/test/schema_test.rb +24 -1
- metadata +23 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e1f56fc7f7100a44079eaaec68a2e7194992fc9c
|
4
|
+
data.tar.gz: 0c3730316fa3f9329b3406cc4fa25ce44331d409
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 461418c693655d9d717f7a9cdf00dc678f6c569655a3eb954678be101c5da93ba1f242cfa25e3a1c3c4683b3dd0e9a0b29467a9813428f5089c39792496adba9
|
7
|
+
data.tar.gz: 83c9808500e1927af547fa5fa2bb679667c9812133a6065dcf6714d6b32f8539e76477d2ccfaf02e26f28e7388c83c8de2b9aefba7c91354c357834798a71cd6
|
data/.travis.yml
CHANGED
data/bin/heroics-generate
CHANGED
@@ -2,10 +2,11 @@
|
|
2
2
|
|
3
3
|
require 'optparse'
|
4
4
|
require 'heroics'
|
5
|
+
require 'open-uri'
|
5
6
|
|
6
7
|
options = {headers: {}, cache_path: nil}
|
7
8
|
option_parser = OptionParser.new do |opts|
|
8
|
-
opts.banner = 'Usage: heroics-generate module_name
|
9
|
+
opts.banner = 'Usage: heroics-generate module_name schema_filepath url'
|
9
10
|
|
10
11
|
opts.on('-h', '--help', 'Display this screen') do
|
11
12
|
puts opts
|
@@ -28,8 +29,8 @@ option_parser.parse!
|
|
28
29
|
if ARGV.length != 3
|
29
30
|
puts option_parser
|
30
31
|
else
|
31
|
-
module_name,
|
32
|
-
schema = Heroics::Schema.new(MultiJson.decode(
|
32
|
+
module_name, schema_filepath, url = ARGV
|
33
|
+
schema = Heroics::Schema.new(MultiJson.decode(open(schema_filepath).read))
|
33
34
|
cache = 'Moneta.new(:Memory)'
|
34
35
|
if options[:cache_path]
|
35
36
|
cache = "Moneta.new(:File, dir: \"#{options[:cache_path]}\")"
|
data/lib/heroics/link.rb
CHANGED
@@ -47,8 +47,8 @@ module Heroics
|
|
47
47
|
path = "#{@path_prefix}#{path}" unless @path_prefix == '/'
|
48
48
|
headers = @default_headers
|
49
49
|
if body
|
50
|
-
headers = headers.merge({'Content-Type' =>
|
51
|
-
body =
|
50
|
+
headers = headers.merge({'Content-Type' => @link_schema.content_type})
|
51
|
+
body = @link_schema.encode(body)
|
52
52
|
end
|
53
53
|
cache_key = "#{path}:#{headers.hash}"
|
54
54
|
if @link_schema.method == :get
|
@@ -59,11 +59,11 @@ module Heroics
|
|
59
59
|
connection = Excon.new(@root_url)
|
60
60
|
response = connection.request(method: @link_schema.method, path: path,
|
61
61
|
headers: headers, body: body,
|
62
|
-
expects: [200, 201, 202, 206, 304])
|
62
|
+
expects: [200, 201, 202, 204, 206, 304])
|
63
63
|
content_type = response.headers['Content-Type']
|
64
64
|
if response.status == 304
|
65
65
|
MultiJson.load(@cache["data:#{cache_key}"])
|
66
|
-
elsif content_type && content_type
|
66
|
+
elsif content_type && content_type =~ /application\/.*json/
|
67
67
|
etag = response.headers['ETag']
|
68
68
|
if etag
|
69
69
|
@cache["etag:#{cache_key}"] = etag
|
data/lib/heroics/schema.rb
CHANGED
@@ -24,7 +24,6 @@ module Heroics
|
|
24
24
|
# @param name [String] The name of the resource.
|
25
25
|
# @raise [SchemaError] Raised if an unknown resource name is provided.
|
26
26
|
def resource(name)
|
27
|
-
resource_schema = @resources[name]
|
28
27
|
if @schema['definitions'].has_key?(name)
|
29
28
|
ResourceSchema.new(@schema, name)
|
30
29
|
else
|
@@ -58,7 +57,8 @@ module Heroics
|
|
58
57
|
def initialize(schema, name)
|
59
58
|
@schema = schema
|
60
59
|
@name = name
|
61
|
-
link_schema = schema['definitions'][name]['links']
|
60
|
+
link_schema = schema['definitions'][name]['links'] || []
|
61
|
+
|
62
62
|
@links = Hash[link_schema.each_with_index.map do |link, link_index|
|
63
63
|
link_name = Heroics.ruby_name(link['title'])
|
64
64
|
[link_name, LinkSchema.new(schema, name, link_index)]
|
@@ -128,6 +128,22 @@ module Heroics
|
|
128
128
|
link_schema['method'].downcase.to_sym
|
129
129
|
end
|
130
130
|
|
131
|
+
# Get the Content-Type for this link.
|
132
|
+
#
|
133
|
+
# @return [String] The Content-Type value
|
134
|
+
def content_type
|
135
|
+
link_schema['encType'] || 'application/json'
|
136
|
+
end
|
137
|
+
|
138
|
+
def encode(body)
|
139
|
+
case content_type
|
140
|
+
when 'application/x-www-form-urlencoded'
|
141
|
+
URI.encode_www_form(body)
|
142
|
+
when /application\/.*json/
|
143
|
+
MultiJson.dump(body)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
131
147
|
# Get the names of the parameters this link expects.
|
132
148
|
#
|
133
149
|
# @return [Array<String>] The parameters.
|
@@ -307,7 +323,8 @@ module Heroics
|
|
307
323
|
# @param [Fixnum,String,TrueClass,FalseClass,Time] The parameter to format.
|
308
324
|
# @return [String] The formatted parameter.
|
309
325
|
def format_parameter(parameter)
|
310
|
-
parameter.instance_of?(Time) ? iso_format(parameter) : parameter.to_s
|
326
|
+
formatted_parameter = parameter.instance_of?(Time) ? iso_format(parameter) : parameter.to_s
|
327
|
+
URI.escape formatted_parameter
|
311
328
|
end
|
312
329
|
|
313
330
|
# Convert a time to an ISO 8601 combined data and time format.
|
data/lib/heroics/version.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -127,6 +127,25 @@ SAMPLE_SCHEMA = {
|
|
127
127
|
'email_field' => {
|
128
128
|
'$ref' => '#/definitions/resource/definitions/email_field'}}}},
|
129
129
|
|
130
|
+
{'description' => 'Submit a sample resource as form data',
|
131
|
+
'encType' => 'application/x-www-form-urlencoded',
|
132
|
+
'href' => '/resource',
|
133
|
+
'method' => 'POST',
|
134
|
+
'rel' => 'submit',
|
135
|
+
'title' => 'Submit',
|
136
|
+
'schema' => {
|
137
|
+
'properties' => {
|
138
|
+
'date_field' => {
|
139
|
+
'$ref' => '#/definitions/resource/definitions/date_field'},
|
140
|
+
'string_field' => {
|
141
|
+
'$ref' => '#/definitions/resource/definitions/string_field'},
|
142
|
+
'boolean_field' => {
|
143
|
+
'$ref' => '#/definitions/resource/definitions/boolean_field'},
|
144
|
+
'uuid_field' => {
|
145
|
+
'$ref' => '#/definitions/resource/definitions/uuid_field'},
|
146
|
+
'email_field' => {
|
147
|
+
'$ref' => '#/definitions/resource/definitions/email_field'}}}},
|
148
|
+
|
130
149
|
{'description' => 'Update a sample resource',
|
131
150
|
'href' => '/resource/{(%23%2Fdefinitions%2Fresource%2Fdefinitions%2Fuuid_field)}',
|
132
151
|
'method' => 'PATCH',
|
data/test/link_test.rb
CHANGED
@@ -38,6 +38,20 @@ class LinkTest < MiniTest::Unit::TestCase
|
|
38
38
|
assert_equal(nil, link.run('44724831-bf66-4bc2-865f-e2c4c2b14c78'))
|
39
39
|
end
|
40
40
|
|
41
|
+
# Link.run URL-escapes special characters in parameters.
|
42
|
+
def test_run_with_parameters_needing_escaping
|
43
|
+
Excon.stub(method: :get) do |request|
|
44
|
+
assert_equal('/resource/foo%23bar', request[:path])
|
45
|
+
Excon.stubs.pop
|
46
|
+
{status: 200, body: ''}
|
47
|
+
end
|
48
|
+
|
49
|
+
schema = Heroics::Schema.new(SAMPLE_SCHEMA)
|
50
|
+
link = Heroics::Link.new('https://example.com',
|
51
|
+
schema.resource('resource').link('info'))
|
52
|
+
assert_equal(nil, link.run('foo#bar'))
|
53
|
+
end
|
54
|
+
|
41
55
|
# Link.run converts Time parameters to UTC before sending them to the
|
42
56
|
# server.
|
43
57
|
def test_run_converts_time_parameters_to_utc
|
@@ -71,6 +85,25 @@ class LinkTest < MiniTest::Unit::TestCase
|
|
71
85
|
assert_equal(nil, link.run(body))
|
72
86
|
end
|
73
87
|
|
88
|
+
# Link.run optionally takes an extra parameter to send in the request body.
|
89
|
+
# It automatically converts the specified object to the specified encoding
|
90
|
+
# type and includes a Content-Type header in the request
|
91
|
+
def test_run_without_parameters_and_with_non_json_request_body
|
92
|
+
body = {'Hello' => 'world!'}
|
93
|
+
Excon.stub(method: :post) do |request|
|
94
|
+
assert_equal('application/x-www-form-urlencoded', request[:headers]['Content-Type'])
|
95
|
+
assert_equal('Hello=world%21', request[:body])
|
96
|
+
Excon.stubs.pop
|
97
|
+
{status: 200, body: ''}
|
98
|
+
end
|
99
|
+
|
100
|
+
schema = Heroics::Schema.new(SAMPLE_SCHEMA)
|
101
|
+
link = Heroics::Link.new('https://example.com',
|
102
|
+
schema.resource('resource').link('submit'))
|
103
|
+
assert_equal(nil, link.run(body))
|
104
|
+
end
|
105
|
+
|
106
|
+
|
74
107
|
# Link.run passes custom headers to the server when they've been provided.
|
75
108
|
def test_run_with_custom_request_headers
|
76
109
|
Excon.stub(method: :get) do |request|
|
@@ -178,7 +211,7 @@ class LinkTest < MiniTest::Unit::TestCase
|
|
178
211
|
assert_equal('/resource', request[:path])
|
179
212
|
Excon.stubs.pop
|
180
213
|
{status: 200,
|
181
|
-
headers: {'Content-Type' => 'application/json;charset=utf-8'},
|
214
|
+
headers: {'Content-Type' => 'application/vnd.api+json;charset=utf-8'},
|
182
215
|
body: MultiJson.dump(body)}
|
183
216
|
end
|
184
217
|
|
@@ -204,6 +237,20 @@ class LinkTest < MiniTest::Unit::TestCase
|
|
204
237
|
assert_equal(body, link.run)
|
205
238
|
end
|
206
239
|
|
240
|
+
# Link.run considers HTTP 204 No Content responses as successful.
|
241
|
+
def test_run_with_no_content_response
|
242
|
+
Excon.stub(method: :delete) do |request|
|
243
|
+
assert_equal("/resource/2013-01-01T08:00:00Z", request[:path])
|
244
|
+
Excon.stubs.pop
|
245
|
+
{status: 204, body: ''}
|
246
|
+
end
|
247
|
+
|
248
|
+
schema = Heroics::Schema.new(SAMPLE_SCHEMA)
|
249
|
+
link = Heroics::Link.new('https://example.com',
|
250
|
+
schema.resource('resource').link('delete'))
|
251
|
+
assert_equal(nil, link.run(Time.parse('2013-01-01 00:00:00-0800')))
|
252
|
+
end
|
253
|
+
|
207
254
|
# Link.run raises an Excon error if anything other than a 200 or 201 HTTP
|
208
255
|
# status code was returned by the server.
|
209
256
|
def test_run_with_failed_request
|
@@ -251,8 +298,9 @@ class LinkTest < MiniTest::Unit::TestCase
|
|
251
298
|
{status: 200}
|
252
299
|
end
|
253
300
|
|
301
|
+
headers = {}
|
254
302
|
cache = Moneta.new(:Memory)
|
255
|
-
cache[
|
303
|
+
cache["etag:/resource:#{headers.hash}"] = 'etag-contents'
|
256
304
|
schema = Heroics::Schema.new(SAMPLE_SCHEMA)
|
257
305
|
link = Heroics::Link.new('https://example.com',
|
258
306
|
schema.resource('resource').link('list'),
|
@@ -287,9 +335,10 @@ class LinkTest < MiniTest::Unit::TestCase
|
|
287
335
|
{status: 304, headers: {'Content-Type' => 'application/json'}}
|
288
336
|
end
|
289
337
|
|
338
|
+
headers = {}
|
290
339
|
cache = Moneta.new(:Memory)
|
291
|
-
cache[
|
292
|
-
cache[
|
340
|
+
cache["etag:/resource:#{headers.hash}"] = 'etag-contents'
|
341
|
+
cache["data:/resource:#{headers.hash}"] = MultiJson.dump(body)
|
293
342
|
schema = Heroics::Schema.new(SAMPLE_SCHEMA)
|
294
343
|
link = Heroics::Link.new('https://example.com',
|
295
344
|
schema.resource('resource').link('list'),
|
data/test/schema_test.rb
CHANGED
@@ -54,7 +54,7 @@ class ResourceSchemaTest < MiniTest::Unit::TestCase
|
|
54
54
|
def test_links
|
55
55
|
schema = Heroics::Schema.new(SAMPLE_SCHEMA)
|
56
56
|
assert_equal(
|
57
|
-
['list', 'info', 'identify_resource', 'create', 'update', 'delete'],
|
57
|
+
['list', 'info', 'identify_resource', 'create', 'submit', 'update', 'delete'],
|
58
58
|
schema.resource('resource').links.map { |link| link.name })
|
59
59
|
end
|
60
60
|
end
|
@@ -167,6 +167,14 @@ class LinkSchemaTest < MiniTest::Unit::TestCase
|
|
167
167
|
link.format_path(['44724831-bf66-4bc2-865f-e2c4c2b14c78']))
|
168
168
|
end
|
169
169
|
|
170
|
+
# LinkSchema.format_path escapes special URL characters in parameters.
|
171
|
+
def test_format_path_with_illegal_literals
|
172
|
+
schema = Heroics::Schema.new(SAMPLE_SCHEMA)
|
173
|
+
link = schema.resource('resource').link('info')
|
174
|
+
assert_equal(['/resource/foobar%25', nil],
|
175
|
+
link.format_path(['foobar%']))
|
176
|
+
end
|
177
|
+
|
170
178
|
# LinkSchema.format_path correctly returns a parameter as a body if a path
|
171
179
|
# doesn't have any parameters.
|
172
180
|
def test_format_path_with_body
|
@@ -224,6 +232,21 @@ class LinkSchemaTest < MiniTest::Unit::TestCase
|
|
224
232
|
link = schema.resource('resource').link('identify_resource')
|
225
233
|
assert_equal('identify-resource', link.pretty_name)
|
226
234
|
end
|
235
|
+
|
236
|
+
# LinkSchema.content_type returns the media type associated with this
|
237
|
+
# resource.
|
238
|
+
def test_content_type
|
239
|
+
schema = Heroics::Schema.new(SAMPLE_SCHEMA)
|
240
|
+
link = schema.resource('resource').link('submit')
|
241
|
+
assert_equal('application/x-www-form-urlencoded', link.content_type)
|
242
|
+
end
|
243
|
+
|
244
|
+
# The content type should default to application/json
|
245
|
+
def test_default_content_type
|
246
|
+
schema = Heroics::Schema.new(SAMPLE_SCHEMA)
|
247
|
+
link = schema.resource('resource').link('identify_resource')
|
248
|
+
assert_equal('application/json', link.content_type)
|
249
|
+
end
|
227
250
|
end
|
228
251
|
|
229
252
|
class DownloadSchemaTest < MiniTest::Unit::TestCase
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: heroics
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- geemus
|
@@ -9,20 +9,20 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2015-03-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - ~>
|
18
|
+
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: '1.3'
|
21
21
|
type: :development
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- - ~>
|
25
|
+
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '1.3'
|
28
28
|
- !ruby/object:Gem::Dependency
|
@@ -43,98 +43,98 @@ dependencies:
|
|
43
43
|
name: rake
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- -
|
46
|
+
- - ">="
|
47
47
|
- !ruby/object:Gem::Version
|
48
48
|
version: '0'
|
49
49
|
type: :development
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- -
|
53
|
+
- - ">="
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: '0'
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
57
|
name: turn
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
59
59
|
requirements:
|
60
|
-
- -
|
60
|
+
- - ">="
|
61
61
|
- !ruby/object:Gem::Version
|
62
62
|
version: '0'
|
63
63
|
type: :development
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
|
-
- -
|
67
|
+
- - ">="
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '0'
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: erubis
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
73
73
|
requirements:
|
74
|
-
- - ~>
|
74
|
+
- - "~>"
|
75
75
|
- !ruby/object:Gem::Version
|
76
76
|
version: 2.7.0
|
77
77
|
type: :runtime
|
78
78
|
prerelease: false
|
79
79
|
version_requirements: !ruby/object:Gem::Requirement
|
80
80
|
requirements:
|
81
|
-
- - ~>
|
81
|
+
- - "~>"
|
82
82
|
- !ruby/object:Gem::Version
|
83
83
|
version: 2.7.0
|
84
84
|
- !ruby/object:Gem::Dependency
|
85
85
|
name: excon
|
86
86
|
requirement: !ruby/object:Gem::Requirement
|
87
87
|
requirements:
|
88
|
-
- -
|
88
|
+
- - ">="
|
89
89
|
- !ruby/object:Gem::Version
|
90
90
|
version: '0'
|
91
91
|
type: :runtime
|
92
92
|
prerelease: false
|
93
93
|
version_requirements: !ruby/object:Gem::Requirement
|
94
94
|
requirements:
|
95
|
-
- -
|
95
|
+
- - ">="
|
96
96
|
- !ruby/object:Gem::Version
|
97
97
|
version: '0'
|
98
98
|
- !ruby/object:Gem::Dependency
|
99
99
|
name: moneta
|
100
100
|
requirement: !ruby/object:Gem::Requirement
|
101
101
|
requirements:
|
102
|
-
- -
|
102
|
+
- - ">="
|
103
103
|
- !ruby/object:Gem::Version
|
104
104
|
version: '0'
|
105
105
|
type: :runtime
|
106
106
|
prerelease: false
|
107
107
|
version_requirements: !ruby/object:Gem::Requirement
|
108
108
|
requirements:
|
109
|
-
- -
|
109
|
+
- - ">="
|
110
110
|
- !ruby/object:Gem::Version
|
111
111
|
version: '0'
|
112
112
|
- !ruby/object:Gem::Dependency
|
113
113
|
name: multi_json
|
114
114
|
requirement: !ruby/object:Gem::Requirement
|
115
115
|
requirements:
|
116
|
-
- -
|
116
|
+
- - ">="
|
117
117
|
- !ruby/object:Gem::Version
|
118
118
|
version: 1.9.2
|
119
119
|
type: :runtime
|
120
120
|
prerelease: false
|
121
121
|
version_requirements: !ruby/object:Gem::Requirement
|
122
122
|
requirements:
|
123
|
-
- -
|
123
|
+
- - ">="
|
124
124
|
- !ruby/object:Gem::Version
|
125
125
|
version: 1.9.2
|
126
126
|
- !ruby/object:Gem::Dependency
|
127
127
|
name: netrc
|
128
128
|
requirement: !ruby/object:Gem::Requirement
|
129
129
|
requirements:
|
130
|
-
- -
|
130
|
+
- - ">="
|
131
131
|
- !ruby/object:Gem::Version
|
132
132
|
version: '0'
|
133
133
|
type: :runtime
|
134
134
|
prerelease: false
|
135
135
|
version_requirements: !ruby/object:Gem::Requirement
|
136
136
|
requirements:
|
137
|
-
- -
|
137
|
+
- - ">="
|
138
138
|
- !ruby/object:Gem::Version
|
139
139
|
version: '0'
|
140
140
|
description: A Ruby client generator for HTTP APIs described with a JSON schema
|
@@ -147,8 +147,8 @@ executables:
|
|
147
147
|
extensions: []
|
148
148
|
extra_rdoc_files: []
|
149
149
|
files:
|
150
|
-
- .gitignore
|
151
|
-
- .travis.yml
|
150
|
+
- ".gitignore"
|
151
|
+
- ".travis.yml"
|
152
152
|
- CONTRIBUTING.md
|
153
153
|
- CONTRIBUTORS.md
|
154
154
|
- Gemfile
|
@@ -192,17 +192,17 @@ require_paths:
|
|
192
192
|
- lib
|
193
193
|
required_ruby_version: !ruby/object:Gem::Requirement
|
194
194
|
requirements:
|
195
|
-
- -
|
195
|
+
- - ">="
|
196
196
|
- !ruby/object:Gem::Version
|
197
197
|
version: '0'
|
198
198
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
199
199
|
requirements:
|
200
|
-
- -
|
200
|
+
- - ">="
|
201
201
|
- !ruby/object:Gem::Version
|
202
202
|
version: '0'
|
203
203
|
requirements: []
|
204
204
|
rubyforge_project:
|
205
|
-
rubygems_version: 2.
|
205
|
+
rubygems_version: 2.2.2
|
206
206
|
signing_key:
|
207
207
|
specification_version: 4
|
208
208
|
summary: A Ruby client generator for HTTP APIs described with a JSON schema
|