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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +2 -0
  5. data/LICENSE +20 -0
  6. data/README.md +357 -0
  7. data/Rakefile +9 -0
  8. data/lib/telestream_cloud.rb +30 -0
  9. data/lib/telestream_cloud/api_authentication.rb +66 -0
  10. data/lib/telestream_cloud/base.rb +111 -0
  11. data/lib/telestream_cloud/config.rb +87 -0
  12. data/lib/telestream_cloud/connection.rb +101 -0
  13. data/lib/telestream_cloud/errors.rb +17 -0
  14. data/lib/telestream_cloud/faraday.rb +84 -0
  15. data/lib/telestream_cloud/flip.rb +9 -0
  16. data/lib/telestream_cloud/modules/associations.rb +55 -0
  17. data/lib/telestream_cloud/modules/builders.rb +45 -0
  18. data/lib/telestream_cloud/modules/destroyers.rb +25 -0
  19. data/lib/telestream_cloud/modules/factory_connection.rb +7 -0
  20. data/lib/telestream_cloud/modules/finders.rb +68 -0
  21. data/lib/telestream_cloud/modules/router.rb +62 -0
  22. data/lib/telestream_cloud/modules/updatable.rb +31 -0
  23. data/lib/telestream_cloud/modules/video_state.rb +16 -0
  24. data/lib/telestream_cloud/proxies/encoding_scope.rb +56 -0
  25. data/lib/telestream_cloud/proxies/profile_scope.rb +7 -0
  26. data/lib/telestream_cloud/proxies/proxy.rb +27 -0
  27. data/lib/telestream_cloud/proxies/scope.rb +94 -0
  28. data/lib/telestream_cloud/proxies/video_scope.rb +28 -0
  29. data/lib/telestream_cloud/resources/encoding.rb +47 -0
  30. data/lib/telestream_cloud/resources/factory.rb +72 -0
  31. data/lib/telestream_cloud/resources/profile.rb +22 -0
  32. data/lib/telestream_cloud/resources/resource.rb +48 -0
  33. data/lib/telestream_cloud/resources/video.rb +39 -0
  34. data/lib/telestream_cloud/telestream_cloud.rb +69 -0
  35. data/lib/telestream_cloud/upload_session.rb +102 -0
  36. data/lib/telestream_cloud/version.rb +3 -0
  37. data/spec/cloud_spec.rb +132 -0
  38. data/spec/encoding_spec.rb +260 -0
  39. data/spec/heroku_spec.rb +32 -0
  40. data/spec/panda_spec.rb +206 -0
  41. data/spec/profile_spec.rb +117 -0
  42. data/spec/spec_helper.rb +18 -0
  43. data/spec/video_spec.rb +399 -0
  44. data/telestream_cloud.gemspec +30 -0
  45. 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
+