telestream_cloud 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +1 -0
- data/Gemfile +2 -0
- data/LICENSE +20 -0
- data/README.md +357 -0
- data/Rakefile +9 -0
- data/lib/telestream_cloud.rb +30 -0
- data/lib/telestream_cloud/api_authentication.rb +66 -0
- data/lib/telestream_cloud/base.rb +111 -0
- data/lib/telestream_cloud/config.rb +87 -0
- data/lib/telestream_cloud/connection.rb +101 -0
- data/lib/telestream_cloud/errors.rb +17 -0
- data/lib/telestream_cloud/faraday.rb +84 -0
- data/lib/telestream_cloud/flip.rb +9 -0
- data/lib/telestream_cloud/modules/associations.rb +55 -0
- data/lib/telestream_cloud/modules/builders.rb +45 -0
- data/lib/telestream_cloud/modules/destroyers.rb +25 -0
- data/lib/telestream_cloud/modules/factory_connection.rb +7 -0
- data/lib/telestream_cloud/modules/finders.rb +68 -0
- data/lib/telestream_cloud/modules/router.rb +62 -0
- data/lib/telestream_cloud/modules/updatable.rb +31 -0
- data/lib/telestream_cloud/modules/video_state.rb +16 -0
- data/lib/telestream_cloud/proxies/encoding_scope.rb +56 -0
- data/lib/telestream_cloud/proxies/profile_scope.rb +7 -0
- data/lib/telestream_cloud/proxies/proxy.rb +27 -0
- data/lib/telestream_cloud/proxies/scope.rb +94 -0
- data/lib/telestream_cloud/proxies/video_scope.rb +28 -0
- data/lib/telestream_cloud/resources/encoding.rb +47 -0
- data/lib/telestream_cloud/resources/factory.rb +72 -0
- data/lib/telestream_cloud/resources/profile.rb +22 -0
- data/lib/telestream_cloud/resources/resource.rb +48 -0
- data/lib/telestream_cloud/resources/video.rb +39 -0
- data/lib/telestream_cloud/telestream_cloud.rb +69 -0
- data/lib/telestream_cloud/upload_session.rb +102 -0
- data/lib/telestream_cloud/version.rb +3 -0
- data/spec/cloud_spec.rb +132 -0
- data/spec/encoding_spec.rb +260 -0
- data/spec/heroku_spec.rb +32 -0
- data/spec/panda_spec.rb +206 -0
- data/spec/profile_spec.rb +117 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/video_spec.rb +399 -0
- data/telestream_cloud.gemspec +30 -0
- metadata +191 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'cgi'
|
3
|
+
require 'time'
|
4
|
+
require 'hmac'
|
5
|
+
require 'hmac-sha2'
|
6
|
+
require 'base64'
|
7
|
+
|
8
|
+
module TelestreamCloud
|
9
|
+
class ApiAuthentication
|
10
|
+
def self.generate_signature(verb, request_uri, host, secret_key, params_given={})
|
11
|
+
# Ensure all param keys are strings
|
12
|
+
params = {}; params_given.each {|k,v| params[k.to_s] = v }
|
13
|
+
|
14
|
+
query_string = canonical_querystring(params)
|
15
|
+
|
16
|
+
string_to_sign = verb.to_s.upcase + "\n" +
|
17
|
+
host.downcase + "\n" +
|
18
|
+
request_uri + "\n" +
|
19
|
+
query_string
|
20
|
+
|
21
|
+
hmac = HMAC::SHA256.new( secret_key )
|
22
|
+
hmac.update( string_to_sign )
|
23
|
+
# chomp is important! the base64 encoded version will have a newline at the end
|
24
|
+
Base64.encode64(hmac.digest).chomp
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# param keys should be strings, not symbols please. return a string joined
|
30
|
+
# by & in canonical order.
|
31
|
+
def self.canonical_querystring(h)
|
32
|
+
_recursion(h).join('&')
|
33
|
+
end
|
34
|
+
# Turns a hash into a query string, returns the query string.
|
35
|
+
# url-encodes everything to Amazon's specifications.
|
36
|
+
def self.hash_to_query(hash)
|
37
|
+
hash.collect{|key, value| url_encode(key) + "=" + url_encode(value) }.join("&")
|
38
|
+
end
|
39
|
+
|
40
|
+
# It's kinda like CGI.escape, except CGI.escape is encoding a tilde when
|
41
|
+
# it ought not to be, so we turn it back. Also space NEEDS to be %20 not +.
|
42
|
+
def self.url_encode(string)
|
43
|
+
CGI.escape(string.to_s).gsub("%7E", "~").gsub("+", "%20")
|
44
|
+
end
|
45
|
+
|
46
|
+
def self._recursion(h, base = nil)
|
47
|
+
pairs = []
|
48
|
+
h.keys.sort.each do |key|
|
49
|
+
value = h[key]
|
50
|
+
if value.kind_of? Hash
|
51
|
+
pairs += _recursion(value, key)
|
52
|
+
else
|
53
|
+
new_pair = nil
|
54
|
+
if base
|
55
|
+
new_pair = "#{base}[#{url_encode(key)}]=#{url_encode(value)}"
|
56
|
+
else
|
57
|
+
new_pair = "#{url_encode(key)}=#{url_encode(value)}"
|
58
|
+
end
|
59
|
+
pairs << new_pair
|
60
|
+
end
|
61
|
+
end
|
62
|
+
pairs
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module TelestreamCloud
|
4
|
+
class Base
|
5
|
+
attr_accessor :attributes, :errors
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
include TelestreamCloud::Router
|
9
|
+
include TelestreamCloud::Builders
|
10
|
+
include TelestreamCloud::Finders
|
11
|
+
|
12
|
+
def initialize(attributes = {})
|
13
|
+
clear_attributes
|
14
|
+
load(attributes)
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def sti_name
|
19
|
+
"#{name.split('::').last}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def changed?
|
24
|
+
!@changed_attributes.empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
def new?
|
28
|
+
id.nil?
|
29
|
+
end
|
30
|
+
|
31
|
+
def id
|
32
|
+
attributes['id']
|
33
|
+
end
|
34
|
+
|
35
|
+
def id=(id)
|
36
|
+
attributes['id'] = id
|
37
|
+
end
|
38
|
+
|
39
|
+
def reload
|
40
|
+
perform_reload
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def inspect
|
45
|
+
attributes_as_nice_string = self.attributes.map {|k,v| "#{k}: #{v.inspect}"}.compact.join(", ")
|
46
|
+
"#<#{self.class} #{attributes_as_nice_string}>"
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_json(*args)
|
50
|
+
MultiJson.encode(self.attributes)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def load_and_reset(response)
|
56
|
+
load_response(response)
|
57
|
+
end
|
58
|
+
|
59
|
+
def perform_reload(args={})
|
60
|
+
raise "RecordNotFound" if new?
|
61
|
+
|
62
|
+
url = self.class.create_rest_url(self.class.one_path, :id => id)
|
63
|
+
response = connection.get(url)
|
64
|
+
load_response(response.merge(args))
|
65
|
+
end
|
66
|
+
|
67
|
+
def clear_attributes
|
68
|
+
@attributes = {}
|
69
|
+
@changed_attributes = {}
|
70
|
+
@errors = []
|
71
|
+
end
|
72
|
+
|
73
|
+
def load(attributes)
|
74
|
+
not_a_response = !(attributes['id'] || attributes[:id])
|
75
|
+
attributes.each do |key, value|
|
76
|
+
@attributes[key.to_s] = value
|
77
|
+
@changed_attributes[key.to_s] = value if not_a_response
|
78
|
+
end
|
79
|
+
true
|
80
|
+
end
|
81
|
+
|
82
|
+
def load_response(response)
|
83
|
+
if response['error'] || response['id'].nil?
|
84
|
+
@errors << APIError.new(response)
|
85
|
+
@loaded = false
|
86
|
+
else
|
87
|
+
clear_attributes
|
88
|
+
load(response)
|
89
|
+
@changed_attributes = {};
|
90
|
+
@loaded = true
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def method_missing(method_symbol, *arguments)
|
95
|
+
method_name = method_symbol.to_s
|
96
|
+
if method_name =~ /(=|\?)$/
|
97
|
+
case $1
|
98
|
+
when '='
|
99
|
+
attributes[$`] = arguments.first
|
100
|
+
@changed_attributes[$`] = arguments.first
|
101
|
+
when '?'
|
102
|
+
!! attributes[$`]
|
103
|
+
end
|
104
|
+
else
|
105
|
+
return attributes[method_name] if attributes.include?(method_name)
|
106
|
+
super
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module TelestreamCloud
|
4
|
+
class Config
|
5
|
+
|
6
|
+
def self.from_panda_url(panda_url)
|
7
|
+
panda_uri = URI.parse(panda_url)
|
8
|
+
|
9
|
+
config = new
|
10
|
+
config.access_key = panda_uri.user
|
11
|
+
config.secret_key = panda_uri.password
|
12
|
+
config.factory_id = panda_uri.path[1..-1]
|
13
|
+
config.api_host = panda_uri.host
|
14
|
+
config.api_port = panda_uri.port
|
15
|
+
config
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.from_hash(auth_params)
|
19
|
+
config = new
|
20
|
+
auth_params.each_key do |key|
|
21
|
+
config.send("#{key}=", auth_params[key])
|
22
|
+
end
|
23
|
+
config
|
24
|
+
end
|
25
|
+
|
26
|
+
def config
|
27
|
+
@config ||= {}
|
28
|
+
end
|
29
|
+
|
30
|
+
[:api_host, :api_port,
|
31
|
+
:access_key, :secret_key,
|
32
|
+
:api_version, :factory_id].each do |attr|
|
33
|
+
define_method "#{attr}" do |value|
|
34
|
+
config["#{attr.to_s}"] = value
|
35
|
+
end
|
36
|
+
|
37
|
+
define_method "#{attr}=" do |value|
|
38
|
+
config["#{attr.to_s}"] = value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_hash
|
43
|
+
config
|
44
|
+
end
|
45
|
+
|
46
|
+
def adapter(adapter_name)
|
47
|
+
TelestreamCloud.adapter = adapter_name
|
48
|
+
end
|
49
|
+
|
50
|
+
def adapter=(adapter_name)
|
51
|
+
TelestreamCloud.adapter = adapter_name
|
52
|
+
end
|
53
|
+
|
54
|
+
# Set the correct api_host for US/EU
|
55
|
+
def region(region)
|
56
|
+
if(region.to_s == 'us')
|
57
|
+
config['api_host'] = US_API_HOST
|
58
|
+
elsif(region.to_s == 'eu')
|
59
|
+
config['api_host'] = EU_API_HOST
|
60
|
+
else
|
61
|
+
raise "Region Unknown"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def validate!
|
66
|
+
errs = validation_errors
|
67
|
+
raise TelestreamCloud::ConfigurationError, errs.join(', ') if errs.any?
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
def valid?
|
72
|
+
validation_errors.empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
def validation_errors
|
76
|
+
err = []
|
77
|
+
if config["access_key"].to_s.empty?
|
78
|
+
err << "access_key is missing"
|
79
|
+
end
|
80
|
+
if config["secret_key"].to_s.empty?
|
81
|
+
err << "secret_key is missing"
|
82
|
+
end
|
83
|
+
err
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module TelestreamCloud
|
2
|
+
|
3
|
+
API_PORT = 443
|
4
|
+
US_API_HOST = "api.pandastream.com"
|
5
|
+
EU_API_HOST = "api-eu.pandastream.com"
|
6
|
+
GCE_API_HOST = "api-gce.pandastream.com"
|
7
|
+
|
8
|
+
class Connection
|
9
|
+
attr_accessor :api_host, :api_port, :access_key, :secret_key, :api_version, :factory_id
|
10
|
+
|
11
|
+
def initialize(auth_params={})
|
12
|
+
params = { :api_host => US_API_HOST, :api_port => API_PORT }.merge!(auth_params)
|
13
|
+
@api_version = '3.0'
|
14
|
+
|
15
|
+
@factory_id = params["factory_id"] || params[:factory_id]
|
16
|
+
@access_key = params["access_key"] || params[:access_key]
|
17
|
+
@secret_key = params["secret_key"] || params[:secret_key]
|
18
|
+
@api_host = params["api_host"] || params[:api_host]
|
19
|
+
@api_port = params["api_port"] || params[:api_port]
|
20
|
+
@prefix = params["prefix_url"] || "v#{api_version}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def http_client
|
24
|
+
TelestreamCloud::HttpClient::Faraday.new(api_url)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Authenticated requests
|
28
|
+
def get(request_uri, params={})
|
29
|
+
sp = signed_params("GET", request_uri, params)
|
30
|
+
http_client.get(request_uri, sp)
|
31
|
+
end
|
32
|
+
|
33
|
+
def post(request_uri, params={})
|
34
|
+
sp = signed_params("POST", request_uri, params)
|
35
|
+
http_client.post(request_uri, sp)
|
36
|
+
end
|
37
|
+
|
38
|
+
def put(request_uri, params={})
|
39
|
+
sp = signed_params("PUT", request_uri, params)
|
40
|
+
http_client.put(request_uri, sp)
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete(request_uri, params={})
|
44
|
+
sp = signed_params("DELETE", request_uri, params)
|
45
|
+
http_client.delete(request_uri, sp)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Signing methods
|
49
|
+
def signed_query(*args)
|
50
|
+
ApiAuthentication.hash_to_query(signed_params(*args))
|
51
|
+
end
|
52
|
+
|
53
|
+
def signed_params(verb, request_uri, params = {}, timestamp_str = nil)
|
54
|
+
auth_params = stringify_keys(params)
|
55
|
+
auth_params['factory_id'] = factory_id unless request_uri =~ /^\/factories/
|
56
|
+
auth_params['access_key'] = access_key
|
57
|
+
auth_params['timestamp'] = timestamp_str || Time.now.utc.iso8601(6)
|
58
|
+
|
59
|
+
params_to_sign = auth_params.reject{|k,v| ['file'].include?(k.to_s)}
|
60
|
+
auth_params['signature'] = ApiAuthentication.generate_signature(verb, request_uri, api_host, secret_key, params_to_sign)
|
61
|
+
auth_params
|
62
|
+
end
|
63
|
+
|
64
|
+
def api_url
|
65
|
+
"#{api_scheme}://#{api_host}:#{api_port}/#{@prefix}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def api_scheme
|
69
|
+
api_port.to_i == 443 ? 'https' : 'http'
|
70
|
+
end
|
71
|
+
|
72
|
+
# Shortcut to setup your bucket
|
73
|
+
def setup_bucket(params={})
|
74
|
+
granting_params = {
|
75
|
+
:s3_videos_bucket => params[:bucket],
|
76
|
+
:aws_access_key => params[:access_key],
|
77
|
+
:aws_secret_key => params[:secret_key]
|
78
|
+
}
|
79
|
+
|
80
|
+
put("/factories/#{@factory_id}.json", granting_params)
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_hash
|
84
|
+
hash = {}
|
85
|
+
[:api_host, :api_port, :access_key, :secret_key, :api_version, :factory_id].each do |a|
|
86
|
+
hash[a] = send(a)
|
87
|
+
end
|
88
|
+
hash
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def stringify_keys(params)
|
94
|
+
params.inject({}) do |options, (key, value)|
|
95
|
+
options[key.to_s] = value
|
96
|
+
options
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module TelestreamCloud
|
2
|
+
|
3
|
+
class Error < StandardError; end
|
4
|
+
|
5
|
+
class APIError < TelestreamCloud::Error
|
6
|
+
def initialize(options)
|
7
|
+
super("#{options['error']}: #{options['message']}")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class ServiceNotAvailable < TelestreamCloud::Error
|
12
|
+
end
|
13
|
+
|
14
|
+
class ConfigurationError < TelestreamCloud::Error
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'multi_json'
|
3
|
+
|
4
|
+
|
5
|
+
module TelestreamCloud
|
6
|
+
module HttpClient
|
7
|
+
class Faraday
|
8
|
+
|
9
|
+
def initialize(api_url)
|
10
|
+
@api_url = api_url
|
11
|
+
end
|
12
|
+
|
13
|
+
def get(request_uri, params)
|
14
|
+
rescue_json_parsing do
|
15
|
+
connection.get do |req|
|
16
|
+
req.url File.join(connection.path_prefix, request_uri), params
|
17
|
+
end.body
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def post(request_uri, params)
|
22
|
+
# multipart upload
|
23
|
+
params['file'] = ::Faraday::UploadIO.new(params['file'], 'multipart/form-data') if params['file']
|
24
|
+
|
25
|
+
rescue_json_parsing do
|
26
|
+
connection.post do |req|
|
27
|
+
req.url File.join(connection.path_prefix, request_uri)
|
28
|
+
req.body = params
|
29
|
+
end.body
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def put(request_uri, params)
|
34
|
+
rescue_json_parsing do
|
35
|
+
connection.put do |req|
|
36
|
+
req.url File.join(connection.path_prefix, request_uri)
|
37
|
+
req.body = params
|
38
|
+
end.body
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def delete(request_uri, params)
|
43
|
+
rescue_json_parsing do
|
44
|
+
connection.delete do |req|
|
45
|
+
req.url File.join(connection.path_prefix, request_uri), params
|
46
|
+
end.body
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def connection
|
53
|
+
@conn ||= ::Faraday.new(:url => @api_url) do |builder|
|
54
|
+
builder.request :multipart
|
55
|
+
builder.request :url_encoded
|
56
|
+
builder.adapter TelestreamCloud.default_adapter
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def rescue_json_parsing(&block)
|
61
|
+
begin
|
62
|
+
data = yield
|
63
|
+
MultiJson.load(data) if data and data != ""
|
64
|
+
rescue MultiJson::DecodeError
|
65
|
+
raise ServiceNotAvailable, data
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
if defined?(Typhoeus)
|
74
|
+
TelestreamCloud.default_adapter = :typhoeus
|
75
|
+
elsif defined?(Excon)
|
76
|
+
TelestreamCloud.default_adapter = :excon
|
77
|
+
elsif defined?(Patron)
|
78
|
+
TelestreamCloud.default_adapter = :patron
|
79
|
+
elsif defined?(NetHttpPersistent)
|
80
|
+
TelestreamCloud.default_adapter = :net_http_persisten
|
81
|
+
else
|
82
|
+
TelestreamCloud.default_adapter = :net_http
|
83
|
+
end
|
84
|
+
|