rest-object 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/rest-object.rb +59 -0
- data/lib/rest-object/gree_err.rb +93 -0
- data/lib/rest-object/gree_json.rb +79 -0
- data/lib/rest-object/gree_logger.rb +101 -0
- data/lib/rest-object/gree_ruby_extensions.rb +73 -0
- metadata +49 -0
data/lib/rest-object.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Module to be included for REST Object
|
2
|
+
#
|
3
|
+
require_relative 'rest-object/gree_json.rb'
|
4
|
+
|
5
|
+
module RestObject
|
6
|
+
|
7
|
+
# response is a Net:HTTPResponse object returned from REST call
|
8
|
+
attr_reader :response, :code, :body
|
9
|
+
|
10
|
+
def initialize(response)
|
11
|
+
@response = response
|
12
|
+
@code = response.code
|
13
|
+
@body = response.body
|
14
|
+
end
|
15
|
+
|
16
|
+
def is_restful?
|
17
|
+
begin
|
18
|
+
return (JSON.parse(@body).class == Hash)
|
19
|
+
rescue
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Verify if @code is 200
|
25
|
+
def is_successful?
|
26
|
+
@code == "200"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Verify if @code is 4xx
|
30
|
+
def is_client_error?
|
31
|
+
end
|
32
|
+
|
33
|
+
# Verify if @code is 5xx
|
34
|
+
def is_server_error?
|
35
|
+
end
|
36
|
+
|
37
|
+
# Verify API response body
|
38
|
+
def verify_rest_body(expected, content_type = :json)
|
39
|
+
if content_type == :json
|
40
|
+
GreeJson.check_json_response(expected, @body)
|
41
|
+
else
|
42
|
+
return (@body == expected)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Verify API response header
|
47
|
+
def verify_rest_header(expected)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
# Unit test
|
54
|
+
if __FILE__ == $0
|
55
|
+
GreeLog.level = Logger::INFO
|
56
|
+
GreeLog.echo_to_screen = true
|
57
|
+
|
58
|
+
end
|
59
|
+
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# convenience functions for dealing with errors and exceptions
|
2
|
+
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
require_relative 'gree_ruby_extensions.rb'
|
6
|
+
require_relative 'gree_logger.rb'
|
7
|
+
|
8
|
+
module GreeErr
|
9
|
+
|
10
|
+
# Construct an error message string saying 2 things are not identical
|
11
|
+
def self.msg_not_identical(value_name, var1, var2)
|
12
|
+
return "#{value_name} is not identical: #1 = #{value_1}, #2 = #{value_2}"
|
13
|
+
end
|
14
|
+
|
15
|
+
# Construct an error message string saying something has an unsupported value
|
16
|
+
def self.msg_unsupported(value_name, value)
|
17
|
+
return "Unsupported #{value_name}: '#{value}'"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Raise a RuntimeError when 2 things are not identical
|
21
|
+
def self.not_identical_raise(value_name, value_1, value_2)
|
22
|
+
raise RuntimeError, self.msg_not_identical(value_name, value_1, value_2), caller
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.raise(*args)
|
26
|
+
_log_message = ""
|
27
|
+
# look for a backtrace to include
|
28
|
+
for _arg in args
|
29
|
+
if _arg.respond_to? :backtrace
|
30
|
+
_log_message << " " << _arg.backtrace.join("\n ")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
# if no backtrace found, add generic
|
34
|
+
if _log_message == ""
|
35
|
+
_log_message << " " << caller.join("\n ")
|
36
|
+
end
|
37
|
+
_log_message = "Exception-> " + args.pretty_inspect + _log_message
|
38
|
+
GreeLog.error(previous_function_name + "()") {
|
39
|
+
_log_message
|
40
|
+
}
|
41
|
+
Kernel.raise *args
|
42
|
+
end
|
43
|
+
|
44
|
+
# Raise an ArgumentError when an argument has unsupported value
|
45
|
+
def self.unsupported_raise(value_name, value)
|
46
|
+
raise ArgumentError, self.msg_unsupported(value_name, value), caller
|
47
|
+
end
|
48
|
+
|
49
|
+
# Execute block, ignoring the specified exception type (default: all exceptions)
|
50
|
+
def self.with_ignored_exceptions(exception_types = StandardError)
|
51
|
+
begin
|
52
|
+
yield
|
53
|
+
rescue exception_types => _the_exception
|
54
|
+
# Ignore the exception
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
# ======================
|
61
|
+
# SELF-TEST
|
62
|
+
# ======================
|
63
|
+
|
64
|
+
if $0 == __FILE__
|
65
|
+
def test
|
66
|
+
GreeLog.handler=Logger.new(
|
67
|
+
File.join(File.expand_path(File.dirname(__FILE__)), 'test-' + Time.now.strftime('%Y-%m-%d-%H%M%S') + '.log')
|
68
|
+
)
|
69
|
+
|
70
|
+
begin
|
71
|
+
GreeErr.raise 'this is an error message'
|
72
|
+
rescue
|
73
|
+
end
|
74
|
+
|
75
|
+
begin
|
76
|
+
GreeErr.raise ArgumentError
|
77
|
+
rescue
|
78
|
+
end
|
79
|
+
|
80
|
+
begin
|
81
|
+
GreeErr.raise ArgumentError, 'this is a message for ArgumentError exception'
|
82
|
+
rescue
|
83
|
+
end
|
84
|
+
|
85
|
+
begin
|
86
|
+
GreeErr.raise
|
87
|
+
rescue
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
test
|
92
|
+
|
93
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# Utilities for JSON handling
|
2
|
+
|
3
|
+
require_relative "gree_logger.rb"
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module GreeJson
|
7
|
+
|
8
|
+
# Validate a JSON response, comparing to an expected hash
|
9
|
+
# TODO: add strict parm to check for extra keys
|
10
|
+
def self.check_json_response(expected, response)
|
11
|
+
begin
|
12
|
+
result = JSON.parse(response)
|
13
|
+
_validate_json_or_json_array(expected, result)
|
14
|
+
rescue => ex
|
15
|
+
GreeLog.puts "EXCEPTION: #{ex.message}\n"
|
16
|
+
GreeLog.puts "EXPECTED\n#{expected.inspect}\n"
|
17
|
+
GreeLog.puts "ACTUAL\n#{response}\nEND"
|
18
|
+
# rethrow the message to remove the recursive stack trace
|
19
|
+
raise ex.message
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self._validate_json_or_json_array(expected, actual)
|
24
|
+
if actual.class == Array
|
25
|
+
_validate_json_array(expected, actual, actual.count)
|
26
|
+
else
|
27
|
+
_validate_json(expected, actual)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self._validate_json(expected, actual)
|
32
|
+
if actual.nil?
|
33
|
+
raise "missing field(s) in response: expected=#{expected} - actual == nil"
|
34
|
+
end
|
35
|
+
expected.each do |k, v|
|
36
|
+
a = actual[k.to_s]
|
37
|
+
#if a.nil?
|
38
|
+
if !actual.has_key?(k.to_s)
|
39
|
+
raise "missing \"#{k}\" property in JSON response, expected: #{v.inspect}"
|
40
|
+
end
|
41
|
+
case v
|
42
|
+
when Hash
|
43
|
+
if a.class != Hash
|
44
|
+
raise "got #{a.class} instead of JSON object for #{k}"
|
45
|
+
end
|
46
|
+
_validate_json(v, a)
|
47
|
+
when Array
|
48
|
+
if a.class != Array
|
49
|
+
raise "got JSON object instead of array for #{k}"
|
50
|
+
end
|
51
|
+
_validate_json_array(v, a, k.to_s)
|
52
|
+
when Regexp
|
53
|
+
a.should match v
|
54
|
+
else
|
55
|
+
a.should == v unless v == :any
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self._validate_json_array(expected, actual, k)
|
61
|
+
if actual.class != Array
|
62
|
+
raise "#{k} in response is not an array as expected"
|
63
|
+
end
|
64
|
+
if expected.count != actual.count
|
65
|
+
raise "Error in #{k} array: expected #{expected.count} items, got #{actual.count}"
|
66
|
+
end
|
67
|
+
expected.each_index do |i|
|
68
|
+
_validate_json(expected[i], actual[i])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
# Unit test
|
76
|
+
if __FILE__ == $0
|
77
|
+
|
78
|
+
end
|
79
|
+
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# Utilities for log handling
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
require 'pp'
|
5
|
+
|
6
|
+
class GreeLog
|
7
|
+
|
8
|
+
@@logger=nil
|
9
|
+
@@echo_to_screen = false
|
10
|
+
|
11
|
+
def self.close
|
12
|
+
@@logger.close
|
13
|
+
@@logger = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.echo_to_screen
|
17
|
+
return @@echo_to_screen
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.echo_to_screen=(value)
|
21
|
+
@@echo_to_screen=value
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.logger
|
25
|
+
return @@logger
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.logger=(logger_object)
|
29
|
+
@@logger=logger_object
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.logging?
|
33
|
+
return !@@logger.nil?
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.method_missing(method_name, *method_args, &block)
|
37
|
+
if @@echo_to_screen
|
38
|
+
puts "[#{Time.now.strftime('%H:%M:%S')}] #{method_name.to_s.upcase} -- #{previous_function_name}: #{method_args.inspect} #{(block ? '{' + block.to_s + '}' : '')}"
|
39
|
+
end
|
40
|
+
if @@logger
|
41
|
+
@@logger.progname = previous_function_name
|
42
|
+
@@logger.send(method_name, *method_args, &block)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# print out a message without escaping the the quotes, newlines ...
|
47
|
+
def self.puts(message)
|
48
|
+
if @@echo_to_screen
|
49
|
+
Kernel.puts "#{message}"
|
50
|
+
end
|
51
|
+
if @@logger
|
52
|
+
@@logger.progname = previous_function_name
|
53
|
+
@@logger.send("info", message)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns the name of the function from which this method was called, by looking at the call stack. If call stack
|
58
|
+
# is empty, returns "(anonymous)"
|
59
|
+
#
|
60
|
+
# def foo
|
61
|
+
# puts current_function_name
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# foo -> "foo"
|
65
|
+
#
|
66
|
+
# current_function_name -> "(anonymous)"
|
67
|
+
#
|
68
|
+
# Used for logging and error reporting, so that generic code can record the name of the currently running function.
|
69
|
+
|
70
|
+
def self.current_function_name
|
71
|
+
return caller()[0] =~ /in `([^']+)'/ ? $1 : '(anonymous)'
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns the name of the function two levels down in the call stack. If call stack
|
75
|
+
# has insufficient depth, returns "(anonymous)"
|
76
|
+
#
|
77
|
+
# def foo
|
78
|
+
# puts previous_function_name
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# def bar
|
82
|
+
# foo
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# bar -> "bar"
|
86
|
+
#
|
87
|
+
# foo -> "(anonymous)"
|
88
|
+
#
|
89
|
+
# previous_function_name => "(anonymous)"
|
90
|
+
#
|
91
|
+
# Used for setting up a centralized error handler that calls a centralized logger.
|
92
|
+
|
93
|
+
def self.previous_function_name
|
94
|
+
if caller().length < 2
|
95
|
+
return '(anonymous)'
|
96
|
+
else
|
97
|
+
return caller()[1] =~ /in `([^']+)'/ ? $1 : '(anonymous)'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# Gree Ruby Extensions
|
2
|
+
# This file provides various convenient extensions to Ruby's built-in objects and libraries
|
3
|
+
|
4
|
+
unless defined? "INFINITY"
|
5
|
+
INFINITY = 1.0/0
|
6
|
+
end
|
7
|
+
|
8
|
+
unless defined? "NaN"
|
9
|
+
NaN = 0.0/0
|
10
|
+
end
|
11
|
+
|
12
|
+
class Array
|
13
|
+
# Same as <tt>Array.detect</tt>, but scans from end of array to beginning.
|
14
|
+
def reverse_detect(&block)
|
15
|
+
self.reverse_each {|_element|
|
16
|
+
if block.call(_element)
|
17
|
+
return _element
|
18
|
+
end
|
19
|
+
}
|
20
|
+
return nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Hash
|
25
|
+
def hash_value_missing?(key)
|
26
|
+
# test if key is missing, or key value is nil or empty string
|
27
|
+
return !self.has_key?(key) || self[key]=="" || self[key].nil?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the name of the function from which this method was called, by looking at the call stack. If call stack
|
32
|
+
# is empty, returns "(anonymous)"
|
33
|
+
#
|
34
|
+
# def foo
|
35
|
+
# puts current_function_name
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# foo -> "foo"
|
39
|
+
#
|
40
|
+
# current_function_name -> "(anonymous)"
|
41
|
+
#
|
42
|
+
# Used for logging and error reporting, so that generic code can record the name of the currently running function.
|
43
|
+
|
44
|
+
def current_function_name
|
45
|
+
return caller()[0] =~ /in `([^']+)'/ ? $1 : '(anonymous)'
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns the name of the function two levels down in the call stack. If call stack
|
49
|
+
# has insufficient depth, returns "(anonymous)"
|
50
|
+
#
|
51
|
+
# def foo
|
52
|
+
# puts previous_function_name
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# def bar
|
56
|
+
# foo
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# bar -> "bar"
|
60
|
+
#
|
61
|
+
# foo -> "(anonymous)"
|
62
|
+
#
|
63
|
+
# previous_function_name => "(anonymous)"
|
64
|
+
#
|
65
|
+
# Used for setting up a centralized error handler that calls a centralized logger.
|
66
|
+
|
67
|
+
def previous_function_name
|
68
|
+
if caller().length < 2
|
69
|
+
return '(anonymous)'
|
70
|
+
else
|
71
|
+
return caller()[1] =~ /in `([^']+)'/ ? $1 : '(anonymous)'
|
72
|
+
end
|
73
|
+
end
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rest-object
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Chaoyi Chen
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-27 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: REST API Object DSL for RESTful API testing
|
15
|
+
email: chaoyi.chen@gree.net
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/rest-object.rb
|
21
|
+
- lib/rest-object/gree_logger.rb
|
22
|
+
- lib/rest-object/gree_ruby_extensions.rb
|
23
|
+
- lib/rest-object/gree_json.rb
|
24
|
+
- lib/rest-object/gree_err.rb
|
25
|
+
homepage: http://rubygems.org/gems/hola
|
26
|
+
licenses: []
|
27
|
+
post_install_message:
|
28
|
+
rdoc_options: []
|
29
|
+
require_paths:
|
30
|
+
- lib
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
32
|
+
none: false
|
33
|
+
requirements:
|
34
|
+
- - ! '>='
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
requirements: []
|
44
|
+
rubyforge_project:
|
45
|
+
rubygems_version: 1.8.24
|
46
|
+
signing_key:
|
47
|
+
specification_version: 3
|
48
|
+
summary: REST API Object DSL for RESTful API testing
|
49
|
+
test_files: []
|