panda 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|