vhx-ruby 0.0.2

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 (76) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +2 -0
  5. data/README.md +59 -0
  6. data/Rakefile +1 -0
  7. data/lib/vhx.rb +66 -0
  8. data/lib/vhx/client.rb +86 -0
  9. data/lib/vhx/error.rb +24 -0
  10. data/lib/vhx/middleware/error_response.rb +32 -0
  11. data/lib/vhx/middleware/oauth2.rb +22 -0
  12. data/lib/vhx/oauth_token.rb +11 -0
  13. data/lib/vhx/objects/authorization.rb +5 -0
  14. data/lib/vhx/objects/collection.rb +8 -0
  15. data/lib/vhx/objects/collection_item.rb +4 -0
  16. data/lib/vhx/objects/customer.rb +8 -0
  17. data/lib/vhx/objects/product.rb +6 -0
  18. data/lib/vhx/objects/site.rb +4 -0
  19. data/lib/vhx/objects/user.rb +10 -0
  20. data/lib/vhx/objects/video.rb +7 -0
  21. data/lib/vhx/objects/video_file.rb +4 -0
  22. data/lib/vhx/utilities/api_operations/create.rb +22 -0
  23. data/lib/vhx/utilities/api_operations/delete.rb +16 -0
  24. data/lib/vhx/utilities/api_operations/list.rb +21 -0
  25. data/lib/vhx/utilities/api_operations/request.rb +17 -0
  26. data/lib/vhx/utilities/api_operations/update.rb +15 -0
  27. data/lib/vhx/utilities/vhx_helper.rb +20 -0
  28. data/lib/vhx/utilities/vhx_list_object.rb +44 -0
  29. data/lib/vhx/utilities/vhx_object.rb +106 -0
  30. data/lib/vhx/version.rb +3 -0
  31. data/spec/client_spec.rb +69 -0
  32. data/spec/fixtures/sample_array_list.json +15 -0
  33. data/spec/fixtures/sample_file_response.json +18 -0
  34. data/spec/fixtures/sample_hash_list.json +23 -0
  35. data/spec/fixtures/sample_package_response.json +185 -0
  36. data/spec/fixtures/sample_site_response.json +26 -0
  37. data/spec/fixtures/sample_user_response.json +21 -0
  38. data/spec/fixtures/sample_video_response.json +51 -0
  39. data/spec/middleware/error_response_spec.rb +11 -0
  40. data/spec/middleware/oauth2_spec.rb +14 -0
  41. data/spec/objects/file_spec.rb +29 -0
  42. data/spec/objects/package_spec.rb +74 -0
  43. data/spec/objects/site_spec.rb +44 -0
  44. data/spec/objects/user_spec.rb +51 -0
  45. data/spec/objects/video_spec.rb +36 -0
  46. data/spec/spec_helper.rb +18 -0
  47. data/spec/test_data.rb +55 -0
  48. data/spec/utilities/vhx_collection_spec.rb +27 -0
  49. data/spec/utilities/vhx_helper_spec.rb +47 -0
  50. data/spec/utilities/vhx_object_spec.rb +53 -0
  51. data/spec/vcr/Vhx_Client/application_user/_refresh_access_token/access_token_changed.yml +57 -0
  52. data/spec/vcr/Vhx_Client/application_user/_refresh_access_token/oauth_token_refreshed.yml +57 -0
  53. data/spec/vcr/Vhx_File/associations/are_present.yml +409 -0
  54. data/spec/vcr/Vhx_Middleware_ErrorResponse/unauthorized_user_credentials.yml +122 -0
  55. data/spec/vcr/Vhx_Middleware_OAuth2/access_token_refresh.yml +797 -0
  56. data/spec/vcr/Vhx_Package/_add_video/with_hypermedia/returns_package_object.yml +51 -0
  57. data/spec/vcr/Vhx_Package/_add_video/with_id/returns_package_object.yml +51 -0
  58. data/spec/vcr/Vhx_Package/_create/returns_package_object.yml +140 -0
  59. data/spec/vcr/Vhx_Package/_find/with_hypermedia.yml +243 -0
  60. data/spec/vcr/Vhx_Package/_find/with_id.yml +243 -0
  61. data/spec/vcr/Vhx_Package/_remove_video/with_hypermedia/returns_success.yml +51 -0
  62. data/spec/vcr/Vhx_Package/_remove_video/with_id/returns_success.yml +51 -0
  63. data/spec/vcr/Vhx_Package/associations/are_present.yml +195 -0
  64. data/spec/vcr/Vhx_Site/_create/returns_site_object.yml +91 -0
  65. data/spec/vcr/Vhx_Site/_find/with_hypermedia.yml +195 -0
  66. data/spec/vcr/Vhx_Site/_find/with_id.yml +195 -0
  67. data/spec/vcr/Vhx_User/_find/with_hypermedia.yml +81 -0
  68. data/spec/vcr/Vhx_User/_find/with_id.yml +81 -0
  69. data/spec/vcr/Vhx_User/_me/returns_user_object.yml +720 -0
  70. data/spec/vcr/Vhx_User/_update/returns_user_object.yml +212 -0
  71. data/spec/vcr/Vhx_VhxObject/associations/cache/retreive_if_available.yml +195 -0
  72. data/spec/vcr/Vhx_VhxObject/associations/falls_back_to_links.yml +310 -0
  73. data/spec/vcr/Vhx_Video/_create/returns_video_object.yml +125 -0
  74. data/spec/vcr/Vhx_Video/associations/are_present.yml +294 -0
  75. data/vhx.gemspec +27 -0
  76. metadata +204 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: beb4e508443c33c4e403dc3af8968963b782b16d
4
+ data.tar.gz: 252a387534bc8e8eb38c6e9189cac2a6e9e501a1
5
+ SHA512:
6
+ metadata.gz: 6e06429c037251651ce2b8ccb192f5e04f46dd0e6311e9c77282841799ad0997de16482be0db7f390b8eee227df018d4de512f149f3616f63357b6b7cc54a5d2
7
+ data.tar.gz: bbe96986befef7b8b2679d51e8c4f4a6d51207402a29a5fd237c6b93ad4ffb66ff64b6f0753e273802c1a8234cfa0781328991a07419c9268f87ae41bd865c49
@@ -0,0 +1,11 @@
1
+ *.gem
2
+ .bundle
3
+ .config
4
+ Gemfile.lock
5
+ doc/
6
+ test/tmp
7
+ test/version_tmp
8
+ tmp
9
+ .tddium*
10
+ .idea
11
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,59 @@
1
+ # VHX Ruby API Client
2
+
3
+ The VHX API is currently Private Beta. You can request an API key by emailing api@vhx.tv.
4
+
5
+ ### Installation
6
+
7
+ `gem install vhx-ruby`
8
+
9
+ ### Documentation
10
+
11
+ Documentation is available at http://dev.vhx.tv/docs/api/ruby.
12
+ Full API reference is available at http://dev.vhx.tv/docs/api?ruby.
13
+
14
+ ## Getting Started
15
+
16
+ Before requesting your first resource, you must setup an instance of the Vhx Client:
17
+
18
+ ```ruby
19
+ vhx = Vhx.setup({ api_key: 'your VHX API key'} )
20
+ ```
21
+
22
+ Here's an example of creating a Vhx resource with payload options. You can handle errors by rescuing Vhx::VhxError.
23
+
24
+ ```ruby
25
+ begin
26
+ # Example Customer Create
27
+ customer = Vhx::Customer.create({
28
+ email: 'customer@email.com',
29
+ name: 'First Last',
30
+ subscription: 'https://api.vhx.tv/subscriptions/1'
31
+ })
32
+ rescue Vhx::VhxError
33
+ # Handle error
34
+ end
35
+ ```
36
+
37
+ ### Resources & methods
38
+
39
+ customers
40
+ * [`create`](http://dev.vhx.tv/docs/api?ruby#create_customer)
41
+ * [`update`](http://dev.vhx.tv/docs/api?ruby#update_customer)
42
+ * [`retrieve`](http://dev.vhx.tv/docs/api?ruby#retrieve_customer)
43
+ * [`list`](http://dev.vhx.tv/docs/api?ruby#list_customers)
44
+
45
+ authorizations
46
+ * [`create`](http://dev.vhx.tv/docs/api?ruby#create_authorization)
47
+
48
+ videos
49
+ * [`create`](http://dev.vhx.tv/docs/api?ruby#create_customer)
50
+ * [`update`](http://dev.vhx.tv/docs/api?ruby#update_customer)
51
+ * [`retrieve`](http://dev.vhx.tv/docs/api?ruby#retrieve_customer)
52
+ * [`list`](http://dev.vhx.tv/docs/api?ruby#list_customers)
53
+
54
+ collections
55
+ * [`create`](http://dev.vhx.tv/docs/api?ruby#create_collection)
56
+ * [`update`](http://dev.vhx.tv/docs/api?ruby#update_collection)
57
+ * [`retrieve`](http://dev.vhx.tv/docs/api?ruby#retrieve_collection)
58
+ * [`list`](http://dev.vhx.tv/docs/api?ruby#list_collections)
59
+ * [`items`](http://dev.vhx.tv/docs/api?ruby#list_collection_items)
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,66 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+
4
+ require "vhx/version"
5
+ require "vhx/error"
6
+ require "vhx/oauth_token"
7
+
8
+ require 'vhx/middleware/error_response'
9
+ require 'vhx/middleware/oauth2'
10
+
11
+ require "vhx/client"
12
+
13
+ require "vhx/utilities/vhx_helper"
14
+ require "vhx/utilities/api_operations/create"
15
+ require "vhx/utilities/api_operations/delete"
16
+ require "vhx/utilities/api_operations/list"
17
+ require "vhx/utilities/api_operations/request"
18
+ require "vhx/utilities/api_operations/update"
19
+
20
+ require "vhx/utilities/vhx_object"
21
+ require "vhx/utilities/vhx_list_object"
22
+
23
+ require "vhx/objects/user"
24
+ require "vhx/objects/collection"
25
+ require "vhx/objects/collection_item"
26
+ require "vhx/objects/product"
27
+ require "vhx/objects/site"
28
+ require "vhx/objects/video"
29
+ require "vhx/objects/video_file"
30
+ require "vhx/objects/customer"
31
+ require "vhx/objects/authorization"
32
+
33
+ module Vhx
34
+ API_BASE_URL = 'https://api.vhx.tv'
35
+
36
+ class << self
37
+ def setup(options = {})
38
+ options[:client_id] ||= @client_id
39
+ options[:client_secret] ||= @client_secret
40
+ options[:api_key] ||= @api_key
41
+ options[:api_base] ||= @api_base_url
42
+ options[:skip_auto_refresh] = @skip_auto_refresh if options[:skip_auto_refresh].nil?
43
+ Vhx.client = Vhx::Client.new(options)
44
+ end
45
+
46
+ def config(config = {})
47
+ @client_id = config[:client_id]
48
+ @client_secret = config[:client_secret]
49
+ @api_key = config[:api_key]
50
+ @skip_auto_refresh = config[:skip_auto_refresh] || false
51
+ @api_base_url = config[:api_base]
52
+ end
53
+
54
+ def client
55
+ @client
56
+ end
57
+
58
+ def client=(new_client)
59
+ @client = new_client
60
+ end
61
+
62
+ def connection
63
+ client ? client.connection : nil
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,86 @@
1
+ module Vhx
2
+ class Client
3
+ attr_reader :client_id, :client_secret, :api_base_url, :oauth_token, :api_key, :connection, :ssl
4
+
5
+ def initialize(options = {})
6
+ options = Hash[options.map{ |k, v| [k.to_sym, v] }]
7
+ @api_base_url = options[:api_base] || API_BASE_URL
8
+ @client_id = options[:client_id]
9
+ @client_secret = options[:client_secret]
10
+ @oauth_token = options[:api_key] ? nil : OAuthToken.new(options, refreshed = false)
11
+ @api_key = options[:api_key]
12
+ @skip_auto_refresh = options[:skip_auto_refresh]
13
+ @ssl = options[:ssl] || {}
14
+ @headers = {}
15
+
16
+ configure_connection
17
+ end
18
+
19
+ def configure_connection
20
+ @connection = Faraday::Connection.new(url: api_base_url, headers: configured_headers, ssl: ssl) do |faraday|
21
+ faraday.request :url_encoded
22
+ faraday.request :json
23
+ faraday.response :logger
24
+ faraday.use Vhx::Middleware::OAuth2, :vhx_client => self unless @skip_auto_refresh
25
+
26
+ faraday.adapter Faraday.default_adapter
27
+
28
+ faraday.use Vhx::Middleware::ErrorResponse
29
+ faraday.response :json
30
+ end
31
+ @connection
32
+ end
33
+
34
+ def configured_headers
35
+ if access_token
36
+ @headers[:Authorization] = "Bearer #{access_token}"
37
+ elsif api_key
38
+ @headers[:Authorization] = Faraday::Request::BasicAuthentication.header(api_key, '')
39
+ end
40
+
41
+ @headers
42
+ end
43
+
44
+ def access_token
45
+ unless oauth_token
46
+ return nil
47
+ end
48
+
49
+ oauth_token.access_token
50
+ end
51
+
52
+ def expired?
53
+ unless oauth_token
54
+ return false
55
+ end
56
+
57
+ oauth_token.expires && oauth_token.expires_at < Time.now.to_i
58
+ end
59
+
60
+ def credentials
61
+ unless oauth_token
62
+ return nil
63
+ end
64
+
65
+ oauth_token.to_h
66
+ end
67
+
68
+ def refresh_access_token!
69
+ conn = @connection.dup
70
+ conn.headers.delete(:Authorization)
71
+ response = conn.post do |req|
72
+ req.url '/oauth/token'
73
+ req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
74
+ req.body = {
75
+ grant_type: 'refresh_token',
76
+ refresh_token: oauth_token.refresh_token,
77
+ client_id: client_id,
78
+ client_secret: client_secret
79
+ }
80
+ end
81
+ @oauth_token = OAuthToken.new(response.body, refreshed = true)
82
+
83
+ configure_connection
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,24 @@
1
+ module Vhx
2
+ class VhxError < StandardError
3
+ attr_reader :response_body, :response_status, :url, :message
4
+
5
+ def initialize(response_body, response_status, url)
6
+ @response_body = response_body
7
+ @response_status = response_status
8
+ @url = url
9
+ @message = response_body.is_a?(Hash) ? response_body['message'] : response_body.to_s
10
+
11
+ super(@message)
12
+ end
13
+ end
14
+
15
+ class BadRequestError < VhxError; end
16
+ class UnauthorizedError < VhxError; end
17
+ class InvalidTokenError < VhxError; end
18
+ class PaymentRequiredError < VhxError; end
19
+ class NotFoundError < VhxError; end
20
+ class NotAcceptableError < VhxError; end
21
+ class ServerError < VhxError; end
22
+
23
+ class InvalidResourceError < StandardError; end
24
+ end
@@ -0,0 +1,32 @@
1
+ module Vhx
2
+ module Middleware
3
+ class ErrorResponse < Faraday::Response::Middleware
4
+ def on_complete(env)
5
+ error_class = case env[:status]
6
+ when 200, 201, 204
7
+ when 304
8
+ when 400
9
+ BadRequestError
10
+ when 401
11
+ if env[:body].fetch('message', '').match(/token/)
12
+ InvalidTokenError
13
+ else
14
+ UnauthorizedError
15
+ end
16
+ when 402
17
+ PaymentRequiredError
18
+ when 404
19
+ NotFoundError
20
+ when 406
21
+ NotAcceptableError
22
+ else
23
+ ServerError
24
+ end
25
+
26
+ if error_class
27
+ raise error_class.new(env[:body], env[:status], env[:url])
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,22 @@
1
+ module Vhx
2
+ module Middleware
3
+ class OAuth2 < Faraday::Middleware
4
+ def call(env)
5
+ orig_env = env.dup
6
+
7
+ begin
8
+ @app.call(env)
9
+ rescue InvalidTokenError
10
+ @vhx_client.refresh_access_token!
11
+ orig_env[:request_headers].merge!(@vhx_client.configured_headers)
12
+ @app.call(orig_env)
13
+ end
14
+ end
15
+
16
+ def initialize(app, options={})
17
+ super(app)
18
+ @vhx_client = options[:vhx_client]
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ class OAuthToken < Struct.new(:access_token, :refresh_token, :expires_at, :expires_in, :expires, :refreshed)
2
+ def initialize(params = {}, refreshed = false)
3
+ params = Hash[params.map{ |k, v| [k.to_sym, v] }]
4
+ self.access_token = params[:access_token]
5
+ self.refresh_token = params[:refresh_token]
6
+ self.expires_at = params[:expires_at] || Time.now.to_i + params[:expires_in].to_i
7
+ self.expires_in = params[:expires_in]
8
+ self.expires = (params[:expires_at] || params[:expires_in]) ? true : false
9
+ self.refreshed = refreshed
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module Vhx
2
+ class Authorization < VhxObject
3
+ include Vhx::ApiOperations::Create
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ module Vhx
2
+ class Collection < VhxObject
3
+ include Vhx::ApiOperations::Create
4
+ include Vhx::ApiOperations::Update
5
+ include Vhx::ApiOperations::Request
6
+ include Vhx::ApiOperations::List
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ module Vhx
2
+ class Collection::Item < VhxObject
3
+ end
4
+ end
@@ -0,0 +1,8 @@
1
+ module Vhx
2
+ class Customer < VhxObject
3
+ include Vhx::ApiOperations::Create
4
+ include Vhx::ApiOperations::Request
5
+ include Vhx::ApiOperations::List
6
+ include Vhx::ApiOperations::Delete
7
+ end
8
+ end
@@ -0,0 +1,6 @@
1
+ module Vhx
2
+ class Product < VhxObject
3
+ include Vhx::ApiOperations::Request
4
+ include Vhx::ApiOperations::List
5
+ end
6
+ end
@@ -0,0 +1,4 @@
1
+ module Vhx
2
+ class Site < VhxObject
3
+ end
4
+ end
@@ -0,0 +1,10 @@
1
+ module Vhx
2
+ class User < VhxObject
3
+ include Vhx::ApiOperations::Request
4
+
5
+ def self.me
6
+ response_json = Vhx.connection.get('/me').body
7
+ self.new(response_json)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ module Vhx
2
+ class Video < VhxObject
3
+ include Vhx::ApiOperations::Create
4
+ include Vhx::ApiOperations::Request
5
+ include Vhx::ApiOperations::List
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ module Vhx
2
+ class Video::File < VhxObject
3
+ end
4
+ end
@@ -0,0 +1,22 @@
1
+ module Vhx
2
+ module ApiOperations
3
+ module Create
4
+ module ClassMethods
5
+ def create(payload)
6
+ klass = get_klass
7
+ response = Vhx.connection.post do |req|
8
+ req.url('/' + klass.downcase + 's') #This url is based purely on VHX's API convention.
9
+ req.body = payload
10
+ end
11
+
12
+ self.new(response.body)
13
+ end
14
+ end
15
+
16
+ def self.included(klass)
17
+ klass.extend(Vhx::HelperMethods)
18
+ klass.extend(ClassMethods)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,16 @@
1
+ module Vhx
2
+ module ApiOperations
3
+ module Delete
4
+ module ClassMethods
5
+ def delete(identifier, payload = {})
6
+ Vhx.connection.delete(get_hypermedia(identifier), payload)
7
+ end
8
+ end
9
+
10
+ def self.included(klass)
11
+ klass.extend(Vhx::HelperMethods)
12
+ klass.extend(ClassMethods)
13
+ end
14
+ end
15
+ end
16
+ end