blix-rest 0.1.30
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +25 -0
- data/README.md +457 -0
- data/lib/blix/rest.rb +145 -0
- data/lib/blix/rest/controller.rb +512 -0
- data/lib/blix/rest/cucumber.rb +8 -0
- data/lib/blix/rest/cucumber/hooks.rb +5 -0
- data/lib/blix/rest/cucumber/request_steps.rb +207 -0
- data/lib/blix/rest/cucumber/resource_steps.rb +28 -0
- data/lib/blix/rest/cucumber/world.rb +273 -0
- data/lib/blix/rest/format.rb +15 -0
- data/lib/blix/rest/format_parser.rb +167 -0
- data/lib/blix/rest/handlebars_assets_fix.rb +10 -0
- data/lib/blix/rest/request_mapper.rb +332 -0
- data/lib/blix/rest/resource_cache.rb +50 -0
- data/lib/blix/rest/response.rb +26 -0
- data/lib/blix/rest/server.rb +208 -0
- data/lib/blix/rest/string_hash.rb +100 -0
- data/lib/blix/rest/version.rb +5 -0
- data/lib/blix/utils.rb +2 -0
- data/lib/blix/utils/misc.rb +62 -0
- data/lib/blix/utils/redis_store.rb +173 -0
- data/lib/blix/utils/yaml_config.rb +74 -0
- metadata +126 -0
@@ -0,0 +1,207 @@
|
|
1
|
+
#========== requests with tokens
|
2
|
+
|
3
|
+
Given(/^(.*?) requests token for service "(.*?)"$/) do |user,service|
|
4
|
+
path = "/myservices/" + service + "/token"
|
5
|
+
send_request('POST',user,path,nil)
|
6
|
+
@_token = valid_response.data["token"]
|
7
|
+
end
|
8
|
+
|
9
|
+
Given(/^(.*?) validates the token for service "(.*?)"$/) do |user,service|
|
10
|
+
path = "/services/" + service + "/validate/" + @_token
|
11
|
+
send_request('GET',user,path,nil)
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
# Given(/^guest gets "(.*?)"( .*)?$/) do |path,condition|
|
16
|
+
# path = add_token_to_path(path,@_token) if condition == " with token"
|
17
|
+
# send_request('GET',"guest",path,nil)
|
18
|
+
# end
|
19
|
+
|
20
|
+
# general requests
|
21
|
+
|
22
|
+
Given(/^(.*?) gets ["'](.*?)["']( with token)?$/) do |user, path, condition|
|
23
|
+
path = add_token_to_path(path,@_token) if condition == " with token"
|
24
|
+
send_request('GET',user,path,nil)
|
25
|
+
end
|
26
|
+
|
27
|
+
Given(/^(.*?) options ["'](.*?)["']( with token)?$/) do |user, path, condition|
|
28
|
+
path = add_token_to_path(path,@_token) if condition == " with token"
|
29
|
+
send_request('OPTIONS',user,path,nil)
|
30
|
+
end
|
31
|
+
|
32
|
+
Given(/^(.*?) posts ["'](.*?)["'] with (.*?)$/) do |user, path, json|
|
33
|
+
send_request('POST',user,path,json)
|
34
|
+
end
|
35
|
+
|
36
|
+
# doc string version
|
37
|
+
Given(/^(.*?) posts ["'](.*?)["'] with$/) do |user, path, json|
|
38
|
+
send_request('POST',user,path,json)
|
39
|
+
end
|
40
|
+
|
41
|
+
Given(/^(.*?) deletes ["'](.*?)["']$/) do |user, path|
|
42
|
+
send_request('DELETE',user,path,nil)
|
43
|
+
end
|
44
|
+
|
45
|
+
Given(/^(.*?) puts ["'](.*?)["'] with (.*?)$/) do |user, path, json|
|
46
|
+
send_request('PUT',user,path,json)
|
47
|
+
end
|
48
|
+
|
49
|
+
# doc string version
|
50
|
+
Given(/^(.*?) puts ["'](.*?)["'] with$/) do |user, path, json|
|
51
|
+
send_request('PUT',user,path,json)
|
52
|
+
end
|
53
|
+
|
54
|
+
# alternative format
|
55
|
+
|
56
|
+
Given(/^I am (.*?)$/) do | string |
|
57
|
+
@_current_user = string.split(' ')[-1]
|
58
|
+
end
|
59
|
+
|
60
|
+
Given(/^I get ["'](.*?)["']$/) do |path|
|
61
|
+
send_request('GET',@_current_user,path,nil)
|
62
|
+
end
|
63
|
+
|
64
|
+
Given(/^I post ["'](.*?)["'] with (.*?)$/) do |path, json|
|
65
|
+
send_request('POST',@_current_user,path,json)
|
66
|
+
end
|
67
|
+
|
68
|
+
Given(/^I delete ["'](.*?)["']$/) do | path|
|
69
|
+
send_request('DELETE',@_current_user,path,nil)
|
70
|
+
end
|
71
|
+
|
72
|
+
Given(/^I put ["'](.*?)["'] with (.*?)$/) do |path, json|
|
73
|
+
send_request('PUT',@_current_user,path,json)
|
74
|
+
end
|
75
|
+
|
76
|
+
# response steps ==============================
|
77
|
+
|
78
|
+
Then(/^the status should be (\d+)$/) do |code|
|
79
|
+
expect(valid_response.status).to eq code.to_i
|
80
|
+
end
|
81
|
+
|
82
|
+
Then(/^there should be an error$/) do
|
83
|
+
expect(valid_response.status.to_s[0,1]).not_to eq '2'
|
84
|
+
expect(valid_response.error).not_to be_nil
|
85
|
+
end
|
86
|
+
|
87
|
+
Then(/^the error message should include ["'](.*?)["']$/) do |field|
|
88
|
+
expect(valid_response.error && (valid_response.error =~ %r/#{field}/)).to_not be nil
|
89
|
+
end
|
90
|
+
|
91
|
+
Given(/^explain$/) do
|
92
|
+
explain
|
93
|
+
end
|
94
|
+
|
95
|
+
Then(/^the data type should be ["'](.*?)["']$/) do |type|
|
96
|
+
if valid_response.data.kind_of? Array
|
97
|
+
expect(valid_response.data[0]["_type"]).to eq type
|
98
|
+
else
|
99
|
+
expect(valid_response.data["_type"]).to eq type
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
Then(/^the data length should be (\d+)$/) do |len|
|
105
|
+
if valid_response.data.kind_of? Array
|
106
|
+
expect(valid_response.data.length).to eq len.to_i
|
107
|
+
else
|
108
|
+
1
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
Then(/^the data length should equal (\d+)$/) do |len|
|
113
|
+
if valid_response.data.kind_of? Array
|
114
|
+
expect(valid_response.data.length).to eq len.to_i
|
115
|
+
else
|
116
|
+
1
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
Then(/^the data "(.*?)" should == (.*?)$/) do |field,val|
|
121
|
+
if valid_data.kind_of? Array
|
122
|
+
data = valid_data[0]
|
123
|
+
else
|
124
|
+
data = valid_data
|
125
|
+
end
|
126
|
+
v = data[field].to_s
|
127
|
+
|
128
|
+
if val =~ %r{^@([^@]*)$}
|
129
|
+
expect(v).to eq store[$1].to_s
|
130
|
+
elsif val =~ %r{^(::)?[A-Z][A-z_a-z:]*$}
|
131
|
+
expect(v).to eq Module.const_get(val).to_s
|
132
|
+
elsif val =~ %r{^['"](.*)['"]$}
|
133
|
+
expect(v).to eq $1
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
Then(/^the data "(.*?)" should equal ["'](.*?)["']$/) do |field,val|
|
139
|
+
if valid_response.data.kind_of? Array
|
140
|
+
data = valid_response.data[0]
|
141
|
+
else
|
142
|
+
data = valid_response.data
|
143
|
+
end
|
144
|
+
expect(data[field].to_s).to eq val
|
145
|
+
end
|
146
|
+
|
147
|
+
Then(/^the data ["'](.*?)["'] should == nil$/) do |field|
|
148
|
+
if valid_response.data.kind_of? Array
|
149
|
+
data = valid_response.data[0]
|
150
|
+
else
|
151
|
+
data = valid_response.data
|
152
|
+
end
|
153
|
+
expect(data[field]).to be nil
|
154
|
+
end
|
155
|
+
|
156
|
+
Then(/^the data ["'](.*?)["'] should equal nil$/) do |field|
|
157
|
+
if valid_response.data.kind_of? Array
|
158
|
+
data = valid_response.data[0]
|
159
|
+
else
|
160
|
+
data =valid_response.data
|
161
|
+
end
|
162
|
+
expect(data[field]).to be nil
|
163
|
+
end
|
164
|
+
|
165
|
+
Then(/^the body should eq ["'](.*?)["']$/) do |val|
|
166
|
+
val.gsub!("\\n","\n")
|
167
|
+
expect(valid_response.body).to eq val
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
|
172
|
+
Then(/^the data should( not)? include ["'](.*?)["']$/) do |state, field|
|
173
|
+
if valid_response.data.kind_of? Array
|
174
|
+
data = valid_response.data[0]
|
175
|
+
else
|
176
|
+
data = valid_response.data
|
177
|
+
end
|
178
|
+
if state == " not"
|
179
|
+
expect(data.key?(field)).to eq false
|
180
|
+
else
|
181
|
+
expect(data.key?(field)).to eq true
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
|
187
|
+
Then(/^store the ["'](.*?)["'] as ["'](.*?)["']$/) do |name,key|
|
188
|
+
if valid_response.data.kind_of?(Array)
|
189
|
+
data = valid_response.data[0]
|
190
|
+
else
|
191
|
+
data = valid_response.data
|
192
|
+
end
|
193
|
+
if data.kind_of?(Hash) && data.key?(name)
|
194
|
+
store[key] = data[name]
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
Given(/^save the "([^"]*)"$/) do |name|
|
199
|
+
if valid_response.data.kind_of?(Array)
|
200
|
+
data = valid_response.data[0]
|
201
|
+
else
|
202
|
+
data = valid_response.data
|
203
|
+
end
|
204
|
+
if data.kind_of?(Hash) && data.key?(name)
|
205
|
+
store[name] = data[name]
|
206
|
+
end
|
207
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class UserHash < Hash
|
2
|
+
def set(k,v)
|
3
|
+
self[k.to_s] = v
|
4
|
+
end
|
5
|
+
|
6
|
+
def get(k)
|
7
|
+
self[k.to_s]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
Given(/^the following users exist:$/) do |table|
|
12
|
+
table.hashes.each do |h|
|
13
|
+
|
14
|
+
name = h["name"] || h["login"]
|
15
|
+
|
16
|
+
next if name=="guest"
|
17
|
+
|
18
|
+
u = UserHash.new.merge!(h)
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
u.set(:pw,h["secret"] || h["password"] || name+"@12345678")
|
23
|
+
|
24
|
+
before_user_create(u,h)
|
25
|
+
users[name] = u
|
26
|
+
after_user_create(u,h)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,273 @@
|
|
1
|
+
# the step definitions are executed in an instance of world so
|
2
|
+
# we can add helper methods for use in the step definitions.
|
3
|
+
|
4
|
+
class RestWorld
|
5
|
+
# the entry point to the rack application to be tested
|
6
|
+
def self.app
|
7
|
+
@_app ||= Rack::Builder.parse_file('config.ru').first
|
8
|
+
end
|
9
|
+
|
10
|
+
# a dummy request to sent to the server
|
11
|
+
def self.request
|
12
|
+
@_req ||= Rack::MockRequest.new(app)
|
13
|
+
end
|
14
|
+
|
15
|
+
# a class to represent a response from the server
|
16
|
+
class Response
|
17
|
+
def initialize(resp)
|
18
|
+
@resp = resp
|
19
|
+
if @resp.header['Content-Type'] == 'application/json'
|
20
|
+
begin
|
21
|
+
@h = MultiJson.load(@resp.body) || {}
|
22
|
+
rescue Exception => e
|
23
|
+
puts 'INVALID RESPONSE BODY=>' + @resp.body
|
24
|
+
raise
|
25
|
+
end
|
26
|
+
else
|
27
|
+
@h = { 'html' => @resp.body }
|
28
|
+
end
|
29
|
+
|
30
|
+
# get_ids_from_hash
|
31
|
+
end
|
32
|
+
|
33
|
+
def [](k)
|
34
|
+
@h[k]
|
35
|
+
end
|
36
|
+
|
37
|
+
def body
|
38
|
+
@resp.body
|
39
|
+
end
|
40
|
+
|
41
|
+
def data
|
42
|
+
@h['data']
|
43
|
+
end
|
44
|
+
|
45
|
+
def error
|
46
|
+
@h['error']
|
47
|
+
end
|
48
|
+
|
49
|
+
def status
|
50
|
+
@resp.status.to_i
|
51
|
+
end
|
52
|
+
|
53
|
+
def header
|
54
|
+
@resp.header || {}
|
55
|
+
end
|
56
|
+
|
57
|
+
def content_type
|
58
|
+
header['Content-Type']
|
59
|
+
end
|
60
|
+
|
61
|
+
def inspect
|
62
|
+
@resp.inspect
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# store cookies for each user here
|
67
|
+
def cookies
|
68
|
+
@_cookies ||= {}
|
69
|
+
@_cookies[@_current_user] ||= []
|
70
|
+
end
|
71
|
+
|
72
|
+
# store current user information here
|
73
|
+
def users
|
74
|
+
@_users ||= {}
|
75
|
+
end
|
76
|
+
|
77
|
+
# store current user tokens here
|
78
|
+
def tokens
|
79
|
+
@_tokens ||= {}
|
80
|
+
end
|
81
|
+
|
82
|
+
# store general information here
|
83
|
+
def store
|
84
|
+
@_store ||= {}
|
85
|
+
end
|
86
|
+
|
87
|
+
def valid_response
|
88
|
+
@_response || raise('no valid response from service')
|
89
|
+
end
|
90
|
+
|
91
|
+
def valid_data
|
92
|
+
@_response && @_response.data || raise("no valid data returned from service:#{@_response.error}")
|
93
|
+
end
|
94
|
+
|
95
|
+
def explain
|
96
|
+
puts "request ==> #{@_verb} #{@_request}"
|
97
|
+
puts "cookies ==> #{cookies.join('; ')}" if cookies.length > 0
|
98
|
+
puts "body ==> #{@_body}" if @_body
|
99
|
+
puts "response ==> #{@_response.inspect}"
|
100
|
+
end
|
101
|
+
|
102
|
+
def before_parse_path(path); end
|
103
|
+
|
104
|
+
def before_parse_body(json); end
|
105
|
+
|
106
|
+
def parse_path(path)
|
107
|
+
path = path.dup
|
108
|
+
|
109
|
+
before_parse_path(path)
|
110
|
+
|
111
|
+
path = path.gsub /\/(@[a-z0-9_]+)/ do |str|
|
112
|
+
str = str[2..-1]
|
113
|
+
id = store[str]
|
114
|
+
raise ":#{str} has not been stored" unless id
|
115
|
+
if id[0] == '/'
|
116
|
+
"#{id}"
|
117
|
+
else
|
118
|
+
"/#{id}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
# and the query part
|
122
|
+
path.gsub /\=(@[a-z0-9_]+)/ do |str|
|
123
|
+
str = str[2..-1]
|
124
|
+
id = store[str]
|
125
|
+
raise ":#{str} has not been stored" unless id
|
126
|
+
|
127
|
+
"=#{id}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def parse_json(json)
|
132
|
+
# replace original format
|
133
|
+
json = json.gsub /:@([a-z0-9_]+)/ do |str|
|
134
|
+
str = str[2..-1]
|
135
|
+
id = store[str]
|
136
|
+
raise ":#{str} has not been stored" unless id
|
137
|
+
|
138
|
+
if id.is_a?(String)
|
139
|
+
":\"#{id}\""
|
140
|
+
else
|
141
|
+
":#{id}"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# replace alternative format
|
146
|
+
json = json.gsub /#\{([a-z0-9_]+)\}/ do |str|
|
147
|
+
str = str[2..-2]
|
148
|
+
id = store[str]
|
149
|
+
raise ":#{str} has not been stored" unless id
|
150
|
+
|
151
|
+
if id.is_a?(String)
|
152
|
+
"\"#{id}\""
|
153
|
+
else
|
154
|
+
"#{id}"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def parse_body(json)
|
160
|
+
json = json.dup
|
161
|
+
before_parse_body(json)
|
162
|
+
parse_json(json)
|
163
|
+
end
|
164
|
+
|
165
|
+
def add_token_to_request
|
166
|
+
return if @_request.include?('token=')
|
167
|
+
if @_user
|
168
|
+
token = @_user.get(:token) || "token12345678-#{@_user.get(:login)}"
|
169
|
+
@_request = if @_request.include?('?')
|
170
|
+
@_request + "&token=#{token}"
|
171
|
+
else
|
172
|
+
@_request + "?token=#{token}"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def add_token_to_path(path,token)
|
178
|
+
return unless token
|
179
|
+
if path.include?('?')
|
180
|
+
path + "&token=" + token
|
181
|
+
else
|
182
|
+
path + "?token=" + token
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def rack_request_headers
|
187
|
+
env = {}
|
188
|
+
env['REMOTE_ADDR'] = '10.0.0.1'
|
189
|
+
env['HTTP_COOKIE'] = cookies.join('; ')
|
190
|
+
env["HTTP_AUTHORIZATION"] = @_auth if @_auth
|
191
|
+
env["HTTP_HOST"] = @_host if @_host
|
192
|
+
env
|
193
|
+
end
|
194
|
+
|
195
|
+
def request
|
196
|
+
RestWorld.request
|
197
|
+
end
|
198
|
+
|
199
|
+
def set_host(name)
|
200
|
+
@_host = name
|
201
|
+
end
|
202
|
+
|
203
|
+
def set_auth_headers(user)
|
204
|
+
raise "invalid user name:#{user}" unless u = users[user]
|
205
|
+
pw = u.get(:pw)
|
206
|
+
raise "iuser name:#{user} has no password!" unless pw
|
207
|
+
str = user + ":" + pw
|
208
|
+
str = Base64.encode64(str)
|
209
|
+
str = "Basic " + str
|
210
|
+
#Rack::MockRequest::DEFAULT_ENV["HTTP_AUTHORIZATION"] = str
|
211
|
+
@_auth = str
|
212
|
+
end
|
213
|
+
|
214
|
+
# save the response for furthur enquiries and store any cookies.
|
215
|
+
def handle_response(raw_response)
|
216
|
+
@_auth = nil
|
217
|
+
@_response = Response.new(raw_response)
|
218
|
+
# add cookies to the cookie jar.
|
219
|
+
#unless @_current_user=="guest"
|
220
|
+
if cookie = @_response.header["Set-Cookie"]
|
221
|
+
parts = cookie.split(';')
|
222
|
+
cookies << parts[0].strip
|
223
|
+
end
|
224
|
+
#end
|
225
|
+
end
|
226
|
+
|
227
|
+
def parse_user(user)
|
228
|
+
user.split(' ')[-1]
|
229
|
+
end
|
230
|
+
|
231
|
+
def send_request(verb, username, path, json)
|
232
|
+
username = parse_user(username)
|
233
|
+
@_verb = verb
|
234
|
+
@_body = json && parse_body(json)
|
235
|
+
@_request = parse_path(path)
|
236
|
+
@_current_user = username
|
237
|
+
|
238
|
+
if username == 'guest'
|
239
|
+
@_user = nil
|
240
|
+
else
|
241
|
+
@_user = users[username]
|
242
|
+
raise "user :#{username} has not been initialized" unless @_user
|
243
|
+
|
244
|
+
pw = @_user.get(:pw)
|
245
|
+
add_token_to_request
|
246
|
+
set_auth_headers(username)
|
247
|
+
end
|
248
|
+
case verb
|
249
|
+
when 'GET'
|
250
|
+
handle_response(request.get(@_request, rack_request_headers))
|
251
|
+
when 'OPTIONS'
|
252
|
+
handle_response(request.options(@_request, rack_request_headers))
|
253
|
+
when 'POST'
|
254
|
+
handle_response(request.post(@_request, rack_request_headers.merge(input: @_body)))
|
255
|
+
when 'PUT'
|
256
|
+
handle_response(request.put(@_request, rack_request_headers.merge(input: @_body)))
|
257
|
+
when 'DELETE'
|
258
|
+
handle_response(request.delete(@_request, rack_request_headers.merge(input: @_body)))
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
# a hook that is called before creating a user
|
263
|
+
def before_user_create(user, hash); end
|
264
|
+
|
265
|
+
# a hook that is called before creating a user
|
266
|
+
def after_user_create(user, hash); end
|
267
|
+
end
|
268
|
+
|
269
|
+
|
270
|
+
|
271
|
+
World do
|
272
|
+
RestWorld.new
|
273
|
+
end
|