vhx-ruby 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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