panda 1.3.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +21 -0
- data/{spec/spec.opts → .rspec} +0 -0
- data/CHANGELOG.md +52 -0
- data/Gemfile +2 -14
- data/README.md +38 -32
- data/Rakefile +9 -27
- data/lib/panda.rb +9 -3
- data/lib/panda/adapters/adapter.rb +4 -0
- data/lib/panda/adapters/faraday.rb +70 -0
- data/lib/panda/adapters/restclient.rb +67 -0
- data/lib/panda/api_authentication.rb +2 -0
- data/lib/panda/base.rb +23 -21
- data/lib/panda/config.rb +58 -0
- data/lib/panda/connection.rb +33 -122
- data/lib/panda/errors.rb +17 -0
- data/lib/panda/modules/builders.rb +18 -10
- data/lib/panda/modules/destroyers.rb +25 -0
- data/lib/panda/modules/finders.rb +10 -4
- data/lib/panda/modules/router.rb +16 -10
- data/lib/panda/modules/updatable.rb +3 -2
- data/lib/panda/modules/video_state.rb +16 -0
- data/lib/panda/modules/viewable.rb +19 -0
- data/lib/panda/panda.rb +41 -20
- data/lib/panda/proxies/proxy.rb +3 -1
- data/lib/panda/proxies/scope.rb +34 -28
- data/lib/panda/resources/cloud.rb +13 -11
- data/lib/panda/resources/encoding.rb +4 -19
- data/lib/panda/resources/resource.rb +2 -12
- data/lib/panda/resources/video.rb +4 -1
- data/lib/panda/version.rb +3 -0
- data/panda.gemspec +22 -105
- data/spec/cloud_spec.rb +44 -35
- data/spec/encoding_spec.rb +28 -9
- data/spec/heroku_spec.rb +15 -5
- data/spec/panda_spec.rb +41 -68
- data/spec/profile_spec.rb +6 -6
- data/spec/spec_helper.rb +3 -4
- data/spec/video_spec.rb +68 -19
- metadata +44 -98
- data/VERSION +0 -1
- data/lib/panda/error.rb +0 -29
- data/lib/panda/modules/short_status.rb +0 -13
data/lib/panda/config.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Panda
|
4
|
+
class Config
|
5
|
+
|
6
|
+
def config
|
7
|
+
@config ||= {}
|
8
|
+
end
|
9
|
+
|
10
|
+
[:api_host, :api_port,
|
11
|
+
:access_key, :secret_key,
|
12
|
+
:api_version, :cloud_id].each do |attr|
|
13
|
+
define_method "#{attr}" do |value|
|
14
|
+
config["#{attr.to_s}"] = value
|
15
|
+
end
|
16
|
+
|
17
|
+
define_method "#{attr}=" do |value|
|
18
|
+
config["#{attr.to_s}"] = value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_hash
|
23
|
+
config
|
24
|
+
end
|
25
|
+
|
26
|
+
def adapter(adapter_name)
|
27
|
+
Panda.adapter = adapter_name
|
28
|
+
end
|
29
|
+
|
30
|
+
def adapter=(adapter_name)
|
31
|
+
Panda.adapter = adapter_name
|
32
|
+
end
|
33
|
+
|
34
|
+
# Setup connection for Heroku
|
35
|
+
def parse_panda_url(panda_url)
|
36
|
+
panda_uri = URI.parse(panda_url)
|
37
|
+
|
38
|
+
config['access_key'] = panda_uri.user
|
39
|
+
config['secret_key'] = panda_uri.password
|
40
|
+
config['cloud_id'] = panda_uri.path[1..-1]
|
41
|
+
config['api_host'] = panda_uri.host
|
42
|
+
config['api_port'] = API_PORT
|
43
|
+
config
|
44
|
+
end
|
45
|
+
|
46
|
+
# Set the correct api_host for US/EU
|
47
|
+
def region(region)
|
48
|
+
if(region.to_s == 'us')
|
49
|
+
config['api_host'] = US_API_HOST
|
50
|
+
elsif(region.to_s == 'eu')
|
51
|
+
config['api_host'] = EU_API_HOST
|
52
|
+
else
|
53
|
+
raise "Region Unknown"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
data/lib/panda/connection.rb
CHANGED
@@ -1,84 +1,40 @@
|
|
1
1
|
module Panda
|
2
|
+
|
3
|
+
API_PORT=443
|
4
|
+
US_API_HOST="api.pandastream.com"
|
5
|
+
EU_API_HOST="api.eu.pandastream.com"
|
6
|
+
|
2
7
|
class Connection
|
3
|
-
attr_accessor :api_host, :api_port, :access_key, :secret_key, :api_version, :cloud_id
|
8
|
+
attr_accessor :api_host, :api_port, :access_key, :secret_key, :api_version, :cloud_id
|
4
9
|
|
5
|
-
|
6
|
-
US_API_HOST="api.pandastream.com"
|
7
|
-
EU_API_HOST="api.eu.pandastream.com"
|
8
|
-
|
9
|
-
def initialize(auth_params={}, options={})
|
10
|
-
@raise_error = false
|
10
|
+
def initialize(auth_params={})
|
11
11
|
@api_version = 2
|
12
|
-
|
13
|
-
|
14
|
-
if auth_params.class == String
|
15
|
-
self.format = options[:format] || options["format"]
|
16
|
-
init_from_uri(auth_params)
|
17
|
-
else
|
18
|
-
self.format = auth_params[:format] || auth_params["format"]
|
19
|
-
init_from_hash(auth_params)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
# Set the correct api_host for US/EU
|
24
|
-
def region=(region)
|
25
|
-
if(region.to_s == "us")
|
26
|
-
self.api_host = US_API_HOST
|
27
|
-
elsif(region.to_s == "eu")
|
28
|
-
self.api_host = EU_API_HOST
|
29
|
-
else
|
30
|
-
raise "Region Unknown"
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
# Setup connection for Heroku
|
35
|
-
def heroku=(url)
|
36
|
-
init_from_uri(url)
|
12
|
+
init_from_hash(auth_params)
|
37
13
|
end
|
38
14
|
|
39
|
-
|
40
|
-
|
41
|
-
@raise_error = bool
|
15
|
+
def adapter
|
16
|
+
@adapter ||= Panda.adapter.new(api_url)
|
42
17
|
end
|
43
|
-
|
44
|
-
# Setup respond type JSON / Hash
|
45
|
-
def format=(ret_format)
|
46
|
-
if ret_format
|
47
|
-
raise "Format unknown" if !["json", "hash"].include?(ret_format.to_s)
|
48
|
-
@format = ret_format.to_s
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
18
|
+
|
52
19
|
# Authenticated requests
|
53
|
-
|
54
20
|
def get(request_uri, params={})
|
55
|
-
|
56
|
-
|
57
|
-
query = signed_query("GET", request_uri, params)
|
58
|
-
body_of @connection[request_uri + '?' + query].get
|
59
|
-
end
|
21
|
+
sp = signed_params("GET", request_uri, params)
|
22
|
+
adapter.get(request_uri, sp)
|
60
23
|
end
|
61
24
|
|
62
25
|
def post(request_uri, params={})
|
63
|
-
|
64
|
-
|
65
|
-
body_of @connection[request_uri].post(signed_params("POST", request_uri, params))
|
66
|
-
end
|
26
|
+
sp = signed_params("POST", request_uri, params)
|
27
|
+
adapter.post(request_uri, sp)
|
67
28
|
end
|
68
29
|
|
69
30
|
def put(request_uri, params={})
|
70
|
-
|
71
|
-
|
72
|
-
body_of @connection[request_uri].put(signed_params("PUT", request_uri, params))
|
73
|
-
end
|
31
|
+
sp = signed_params("PUT", request_uri, params)
|
32
|
+
adapter.put(request_uri, sp)
|
74
33
|
end
|
75
34
|
|
76
35
|
def delete(request_uri, params={})
|
77
|
-
|
78
|
-
|
79
|
-
query = signed_query("DELETE", request_uri, params)
|
80
|
-
body_of @connection[request_uri + '?' + query].delete
|
81
|
-
end
|
36
|
+
sp = signed_params("DELETE", request_uri, params)
|
37
|
+
adapter.delete(request_uri, sp)
|
82
38
|
end
|
83
39
|
|
84
40
|
# Signing methods
|
@@ -88,9 +44,9 @@ module Panda
|
|
88
44
|
|
89
45
|
def signed_params(verb, request_uri, params = {}, timestamp_str = nil)
|
90
46
|
auth_params = stringify_keys(params)
|
91
|
-
auth_params['cloud_id'] = cloud_id
|
47
|
+
auth_params['cloud_id'] = cloud_id unless request_uri =~ /^\/clouds/
|
92
48
|
auth_params['access_key'] = access_key
|
93
|
-
auth_params['timestamp'] = timestamp_str || Time.now.iso8601(6)
|
49
|
+
auth_params['timestamp'] = timestamp_str || Time.now.utc.iso8601(6)
|
94
50
|
|
95
51
|
params_to_sign = auth_params.reject{|k,v| ['file'].include?(k.to_s)}
|
96
52
|
auth_params['signature'] = ApiAuthentication.generate_signature(verb, request_uri, api_host, secret_key, params_to_sign)
|
@@ -98,10 +54,10 @@ module Panda
|
|
98
54
|
end
|
99
55
|
|
100
56
|
def api_url
|
101
|
-
"#{
|
57
|
+
"#{api_scheme}://#{api_host}:#{api_port}/#{@prefix}"
|
102
58
|
end
|
103
59
|
|
104
|
-
def
|
60
|
+
def api_scheme
|
105
61
|
api_port == 443 ? 'https' : 'http'
|
106
62
|
end
|
107
63
|
|
@@ -109,8 +65,8 @@ module Panda
|
|
109
65
|
def setup_bucket(params={})
|
110
66
|
granting_params = {
|
111
67
|
:s3_videos_bucket => params[:bucket],
|
112
|
-
:
|
113
|
-
:
|
68
|
+
:aws_access_key => params[:access_key],
|
69
|
+
:aws_secret_key => params[:secret_key]
|
114
70
|
}
|
115
71
|
|
116
72
|
put("/clouds/#{@cloud_id}.json", granting_params)
|
@@ -125,61 +81,16 @@ module Panda
|
|
125
81
|
end
|
126
82
|
|
127
83
|
private
|
128
|
-
def stringify_keys(params)
|
129
|
-
params.inject({}) do |options, (key, value)|
|
130
|
-
options[key.to_s] = value
|
131
|
-
options
|
132
|
-
end
|
133
|
-
end
|
134
84
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
format_to(e.http_body)
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
# API change on rest-client 1.4
|
144
|
-
def body_of(response)
|
145
|
-
json_response = response.respond_to?(:body) ? response.body : response
|
146
|
-
format_to(json_response)
|
147
|
-
end
|
148
|
-
|
149
|
-
def format_to(response)
|
150
|
-
begin
|
151
|
-
if self.format == "json"
|
152
|
-
response
|
153
|
-
elsif defined?(ActiveSupport::JSON)
|
154
|
-
ActiveSupport::JSON.decode(response)
|
155
|
-
else
|
156
|
-
JSON.parse(response)
|
157
|
-
end
|
158
|
-
rescue JSON::ParserError => e
|
159
|
-
# if not used with PandaResources
|
160
|
-
# don't raise Service Not Available because
|
161
|
-
# maybe the host, the url, or anything is wrongly setup
|
162
|
-
if @raise_error
|
163
|
-
raise ServiceNotAvailable.new
|
164
|
-
else
|
165
|
-
raise e
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
def init_from_uri(uri)
|
171
|
-
heroku_uri = URI.parse(uri)
|
172
|
-
|
173
|
-
@access_key = heroku_uri.user
|
174
|
-
@secret_key = heroku_uri.password
|
175
|
-
@cloud_id = heroku_uri.path[1..-1]
|
176
|
-
@api_host = heroku_uri.host
|
177
|
-
@api_port = heroku_uri.port
|
178
|
-
@prefix = "v#{api_version}"
|
85
|
+
def stringify_keys(params)
|
86
|
+
params.inject({}) do |options, (key, value)|
|
87
|
+
options[key.to_s] = value
|
88
|
+
options
|
179
89
|
end
|
90
|
+
end
|
180
91
|
|
181
|
-
|
182
|
-
|
92
|
+
def init_from_hash(hash_params)
|
93
|
+
params = { :api_host => US_API_HOST, :api_port => API_PORT }.merge!(hash_params)
|
183
94
|
|
184
95
|
@cloud_id = params["cloud_id"] || params[:cloud_id]
|
185
96
|
@access_key = params["access_key"] || params[:access_key]
|
@@ -187,7 +98,7 @@ module Panda
|
|
187
98
|
@api_host = params["api_host"] || params[:api_host]
|
188
99
|
@api_port = params["api_port"] || params[:api_port]
|
189
100
|
@prefix = params["prefix_url"] || "v#{api_version}"
|
190
|
-
|
101
|
+
end
|
191
102
|
end
|
192
103
|
end
|
193
104
|
|
data/lib/panda/errors.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Panda
|
2
|
+
|
3
|
+
class Error < StandardError; end
|
4
|
+
|
5
|
+
class APIError < Panda::Error
|
6
|
+
def initialize(options)
|
7
|
+
super("#{options['error']}: #{options['message']}")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class ServiceNotAvailable < Panda::Error
|
12
|
+
def initialize
|
13
|
+
super("ServiceNotAvailable")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -2,35 +2,43 @@ module Panda
|
|
2
2
|
module Builders
|
3
3
|
|
4
4
|
def self.included(base)
|
5
|
-
base.extend(
|
6
|
-
base.extend(DeleteBuilder)
|
5
|
+
base.extend(ClassMethods)
|
7
6
|
end
|
8
7
|
|
9
|
-
module
|
8
|
+
module ClassMethods
|
10
9
|
|
11
|
-
def create(attributes)
|
10
|
+
def create(attributes={})
|
12
11
|
resource = build_resource(attributes)
|
12
|
+
yield resource if block_given?
|
13
|
+
|
13
14
|
resource.create
|
14
15
|
resource
|
15
16
|
end
|
16
17
|
|
17
|
-
def create!(attributes)
|
18
|
+
def create!(attributes={})
|
18
19
|
resource = build_resource(attributes)
|
20
|
+
yield resource if block_given?
|
21
|
+
|
19
22
|
resource.create!
|
20
23
|
resource
|
21
24
|
end
|
22
25
|
|
23
26
|
private
|
27
|
+
|
24
28
|
def build_resource(attributes)
|
25
29
|
Panda::const_get("#{sti_name}").new(attributes.merge(:cloud_id => cloud.id))
|
26
30
|
end
|
27
31
|
end
|
28
32
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
33
|
+
def create
|
34
|
+
raise "Can't create attribute. Already have an id=#{attributes['id']}" if attributes['id']
|
35
|
+
uri = replace_pattern_with_self_variables(self.class.many_path)
|
36
|
+
response = connection.post(uri, attributes)
|
37
|
+
load_and_reset(response)
|
38
|
+
end
|
39
|
+
|
40
|
+
def create!
|
41
|
+
create || raise(errors.last)
|
34
42
|
end
|
35
43
|
|
36
44
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Panda
|
2
|
+
module Destroyers
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
|
10
|
+
def delete(id)
|
11
|
+
uri = json_path(create_rest_url(one_path,{:id =>id}))
|
12
|
+
response = connection.delete(uri)
|
13
|
+
response['deleted'] == 'ok'
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete
|
19
|
+
uri = replace_pattern_with_self_variables(self.class.one_path)
|
20
|
+
response = connection.delete(uri)
|
21
|
+
!!response['deleted']
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -1,16 +1,22 @@
|
|
1
1
|
module Panda
|
2
2
|
module Finders
|
3
3
|
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(FindOne)
|
6
|
+
base.extend(FindMany)
|
7
|
+
end
|
8
|
+
|
4
9
|
module FindOne
|
5
10
|
|
6
11
|
def find(id)
|
12
|
+
raise 'find method requires a correct value' if id.nil? || id == ''
|
7
13
|
find_by_path(one_path, {:id => id})
|
8
14
|
end
|
9
15
|
|
10
16
|
def find_object_by_path(url, map={})
|
11
|
-
|
12
|
-
params =
|
13
|
-
|
17
|
+
rest_url = create_rest_url(url, map)
|
18
|
+
params = extract_unmapped_variables(url, map)
|
19
|
+
connection.get(rest_url, params)
|
14
20
|
end
|
15
21
|
|
16
22
|
def find_by_path(url, map={})
|
@@ -22,7 +28,7 @@ module Panda
|
|
22
28
|
elsif object['id']
|
23
29
|
kclass.new(object)
|
24
30
|
else
|
25
|
-
|
31
|
+
raise APIError.new(object)
|
26
32
|
end
|
27
33
|
end
|
28
34
|
|
data/lib/panda/modules/router.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module Panda
|
2
2
|
module Router
|
3
|
-
DEFAULT_FORMAT = "json"
|
4
3
|
VAR_PATTERN = /:\w+/
|
5
4
|
|
6
5
|
def self.included(base)
|
@@ -31,24 +30,31 @@ module Panda
|
|
31
30
|
prefix_path + end_path
|
32
31
|
end
|
33
32
|
|
34
|
-
def
|
35
|
-
|
33
|
+
def create_rest_url(url, map)
|
34
|
+
new_url = replace_pattern_with_variables(url, map)
|
35
|
+
json_path(new_url)
|
36
36
|
end
|
37
|
+
|
38
|
+
private
|
37
39
|
|
38
|
-
def
|
40
|
+
def replace_pattern_with_variables(url, map)
|
41
|
+
new_url = url.clone
|
42
|
+
new_url.gsub(VAR_PATTERN){|key| map[key[1..-1].to_sym] || map[key[1..-1].to_s]}
|
43
|
+
end
|
44
|
+
|
45
|
+
def extract_unmapped_variables(url, map)
|
39
46
|
params = map.clone
|
40
|
-
url.
|
47
|
+
url.scan(VAR_PATTERN).map{|key| params.reject!{|k,v| k==key[1..-1] } }
|
41
48
|
params
|
42
49
|
end
|
43
50
|
|
44
|
-
def
|
45
|
-
|
51
|
+
def json_path(uri)
|
52
|
+
uri + ".json"
|
46
53
|
end
|
47
|
-
|
48
54
|
end
|
49
55
|
|
50
|
-
def
|
51
|
-
self.class.
|
56
|
+
def replace_pattern_with_self_variables(url)
|
57
|
+
self.class.create_rest_url(url, attributes)
|
52
58
|
end
|
53
59
|
|
54
60
|
end
|