trailer_vote-api 0.6.1 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3eeb024b539fc6f3d91d8b8b7b0856ace8176c2f
4
- data.tar.gz: 824f168e749e1adfd15dad052391d94a07543543
3
+ metadata.gz: b341c5f1af4fab01aaa581d934e82ca906802831
4
+ data.tar.gz: 84d7734f7f69b71c192436a6c990926f0849ec5d
5
5
  SHA512:
6
- metadata.gz: 2c6249c188cab7e34fa49506fd9b96fb40a7d28d98356a124b77ef60ef1f452e3122aff23e7d7decce052a731621cd3c43bdfba90346e782db92a90e3162db0c
7
- data.tar.gz: b43f3f1c5bb2d5b81ff61f248d1bdc343e198520f1dfce694afbae6b5208f74c0a0e9db389625b6810bd02212423da4de5348840fb1cfdc0fc26e4b35dcf76e1
6
+ metadata.gz: 46a655c726b94de729886ce610e6d0271ec75622a712656a680b5dd410a0519a07ae354cffb40f2b0df62e07561e17a65bf3b14dbeb8ac197897445257776904
7
+ data.tar.gz: 359f30bbb77df6eaf57617c5cd3007bc6b67156f35099281857f97eab8e5a92f0633e259105511d6dd6a88bb9d50e67dbfc8bba9c028b9710d08e89d96afb91a
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ # 0.7.0
2
+
3
+ - Guards network errors during all API calls
4
+ - Add `NetworkError` and the subclasses `TimeoutError`, `ConnectionError`
5
+ - Add `back` links to most API calls
6
+ - Add `MediaTypeUnknown` error for decoding (and encoding) unknown media types
7
+ - Add `application/json` fallback support for hostile server responses
8
+ - Add `text/html` fallback support
9
+ - Fix a bug with a missing `product` attribute
10
+ - Fix an unsupported media type causing `ErrorsResponse` to break
11
+
1
12
  # 0.6.1
2
13
 
3
14
  - Always coerce body into string first because `Oj` might choke on chunked body.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- trailer_vote-api (0.6.1)
4
+ trailer_vote-api (0.7.0)
5
5
  http (>= 3.3.0, < 4.x)
6
6
  oj (>= 3.6, < 4.x)
7
7
  trailer_vote-media_types (>= 0.6.1, < 1)
@@ -7,6 +7,7 @@ HTTP_KLAZZ = HTTP.use(:auto_inflate)
7
7
 
8
8
  require 'trailer_vote/api/version'
9
9
  require 'trailer_vote/api/type_registry'
10
+ require 'trailer_vote/api/fallback_content_types'
10
11
  require 'trailer_vote/api/errors'
11
12
  require 'trailer_vote/api/configuration'
12
13
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'trailer_vote/api'
4
+
4
5
  require 'trailer_vote/api/place'
5
6
  require 'trailer_vote/api/place/create'
6
7
  require 'trailer_vote/api/place/find'
@@ -8,8 +9,11 @@ require 'trailer_vote/api/product'
8
9
  require 'trailer_vote/api/product/create'
9
10
  require 'trailer_vote/api/product/find'
10
11
  require 'trailer_vote/api/product/lookup'
12
+ require 'trailer_vote/api/product/image'
11
13
  require 'trailer_vote/api/product/image/create'
12
14
  require 'trailer_vote/api/product/image/find'
15
+ require 'trailer_vote/api/product/place'
13
16
  require 'trailer_vote/api/product/place/link'
17
+ require 'trailer_vote/api/product/video'
14
18
  require 'trailer_vote/api/product/video/create'
15
19
  require 'trailer_vote/api/product/video/find'
@@ -13,6 +13,10 @@ module TrailerVote
13
13
  end
14
14
  end
15
15
 
16
+ # Execute the call (and all dependent calls)
17
+ #
18
+ # @see #forward_klazz
19
+ # @see #redirect_klazz
16
20
  def call(**_opts)
17
21
  raise format('Missing implementation of #args in %<name>s', self.class.name)
18
22
  end
@@ -23,14 +27,40 @@ module TrailerVote
23
27
  configuration.client
24
28
  end
25
29
 
30
+ # Class used to branch forward to in case of a HTTP 307 or 308.
31
+ #
32
+ # @see #branch
33
+ # @see #forward
34
+ #
35
+ # @private
36
+ # @return [Class]
26
37
  def forward_klazz
27
38
  self.class
28
39
  end
29
40
 
41
+ # Class used to branch redirect to in case of a HTTP 200, 201, 204, 301, 302, 303 or 304.
42
+ #
43
+ # When the status does not allow for redirecting, instead sends the retrieved result to the redirected class.
44
+ #
45
+ # @see #branch
46
+ # @see #redirect
47
+ #
48
+ # @private
49
+ # @return [Class]
30
50
  def redirect_klazz
31
51
  self.class
32
52
  end
33
53
 
54
+ def guard_network_errors
55
+ yield
56
+ rescue HTTP::ConnectionError => err
57
+ raise ConnectionError, err
58
+ rescue HTTP::TimeoutError => err
59
+ raise TimeoutError, err
60
+ rescue HTTP::Error => err
61
+ raise NetworkError, err
62
+ end
63
+
34
64
  def branch(result, data: nil)
35
65
  raise_on_error(result)
36
66
  forward(result, data: data) || redirect(result)
@@ -14,23 +14,35 @@ module TrailerVote
14
14
  end
15
15
  end
16
16
 
17
+ # Return the {Links} for the result
18
+ # @return [Links] the links
17
19
  def links
18
20
  # TODO: or headers
19
21
  @links ||= Links.new(data['_links'])
20
22
  end
21
23
 
24
+ # Decode the result via {TypeRegistry}. This also takes care of MediaTypes validation.
25
+ #
26
+ # @see #decode
27
+ # @return [Hash, Array] the decoded response
22
28
  def to_h
23
29
  @to_h ||= TrailerVote::Api.decode(call.result)
24
30
  end
25
31
 
32
+ # Return the HTTP status
33
+ # @return [Numeric] the HTTP status
26
34
  def to_i
27
35
  call.result.status
28
36
  end
29
37
 
38
+ # Return the ETag value
39
+ # @return [String, NilClass] the etag or nil
30
40
  def etag
31
41
  call.result[Headers::ETAG]
32
42
  end
33
43
 
44
+ # Return the decoded result inner object
45
+ # @return [Object] the inner object
34
46
  def data
35
47
  raise format('Missing implementation of #data in %<name>s', self.class.name)
36
48
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'trailer_vote/media_types'
3
4
  require 'trailer_vote/api/composable/get'
4
5
 
5
6
  module TrailerVote
@@ -34,8 +35,9 @@ module TrailerVote
34
35
 
35
36
  def call(url: resolve_url)
36
37
  return self if ok? || !url
37
- merge(resolve_client.headers(Headers::ACCEPT => ACCEPT).get(url))
38
- # TODO: result.raise_for_status
38
+ guard_network_errors do
39
+ merge(resolve_client.headers(Headers::ACCEPT => ACCEPT).get(url))
40
+ end
39
41
  end
40
42
 
41
43
  private
@@ -1,9 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'trailer_vote/media_types'
4
+
3
5
  module TrailerVote
4
6
  module Api
5
7
 
6
8
  class Error < RuntimeError; end
9
+ class UnknownMediaType < Error; end
10
+ class NetworkError < Error; end
11
+ class ConnectionError < NetworkError; end
12
+ class TimeoutError < NetworkError; end
7
13
 
8
14
  class EncodeError < Error
9
15
  def initialize(media_type:, source:)
@@ -47,7 +53,7 @@ module TrailerVote
47
53
 
48
54
  def data
49
55
  @data ||= TrailerVote::Api.decode(result)
50
- rescue DecodeError
56
+ rescue DecodeError, UnknownMediaType
51
57
  # noinspection RubyStringKeysInHashInspection
52
58
  @data = { 'errors' => [{ 'message': result.status.reason }] }
53
59
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'oj'
4
+ require 'trailer_vote/api/type_registry'
5
+
6
+ module TrailerVote
7
+ module Api
8
+ module JsonTypeAdapter
9
+ module_function
10
+
11
+ def encode(obj)
12
+ Oj.dump(obj, mode: :compat)
13
+ rescue Oj::Error => err
14
+ raise EncodeError.new(media_type: 'application/json', source: err)
15
+ end
16
+
17
+ def decode(obj)
18
+ Oj.load(obj, mode: :strict)
19
+ rescue Oj::Error => err
20
+ raise DecodeError.new(media_type: 'application/json', source: err)
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ TrailerVote::Api::TypeRegistry['application/json'] = TrailerVote::Api::JsonTypeAdapter
27
+ TrailerVote::Api::TypeRegistry.shortcut('application/json', :json)
28
+ TrailerVote::Api::TypeRegistry.shortcut('application/json', 'text/json')
29
+
30
+ module TrailerVote
31
+ module Api
32
+ module HtmlTypeAdapter
33
+ module_function
34
+
35
+ def encode(obj)
36
+ return obj if obj.is_a?(String)
37
+ raise EncodeError.new(
38
+ media_type: 'text/html',
39
+ source: ArgumentError.new('HTML must be passed in as a HTML string')
40
+ )
41
+ end
42
+
43
+ def decode(str)
44
+ str
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ TrailerVote::Api::TypeRegistry['text/html'] = TrailerVote::Api::HtmlTypeAdapter
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TrailerVote
2
4
  module Api
3
5
  class Links
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'trailer_vote/media_types'
3
4
  require 'trailer_vote/api/configuration'
4
5
 
5
6
  module TrailerVote
@@ -9,6 +10,10 @@ module TrailerVote
9
10
  self.configuration = configuration
10
11
  end
11
12
 
13
+ def back
14
+ configuration
15
+ end
16
+
12
17
  private
13
18
 
14
19
  attr_accessor :configuration
@@ -8,6 +8,8 @@ module TrailerVote
8
8
  module Api
9
9
  class Place
10
10
  class Find
11
+
12
+ # @return [TrailerVote::Api::Place::Create] the api to create a child place
11
13
  def create
12
14
  Create.new(configuration: configuration, place: self)
13
15
  end
@@ -22,20 +24,36 @@ module TrailerVote
22
24
 
23
25
  ACCEPT = [SUCCESS.to_s, FAILURE.to_s(0.1)].join(', ').freeze
24
26
 
27
+ # @private
25
28
  def initialize(configuration:, place:)
26
29
  self.configuration = configuration
27
30
  self.place = place
28
31
  end
29
32
 
33
+ # @return [TrailerVote::Api::Place::Find] the found place
34
+ def back
35
+ place
36
+ end
37
+
38
+ # Create a place
39
+ #
40
+ # @see https://www.rubydoc.info/gems/trailer_vote-media_types/TrailerVote/MediaTypes/Place TrailerVote::MediaTypes::Place
41
+ #
42
+ # @param [String, Hash] data the data conform to the Place media type
43
+ # @param [String] url (#resolve_url result) the url to post to
44
+ #
45
+ # @return [TrailerVote::Api::Place::Find]
30
46
  def call(data:, url: resolve_url)
31
- body = encode(data)
32
- branch(
33
- resolve_client.headers(
34
- Headers::ACCEPT => ACCEPT,
35
- Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}"
36
- ).post(url, body: body),
37
- data: data
38
- )
47
+ guard_network_errors do
48
+ body = encode(data)
49
+ branch(
50
+ resolve_client.headers(
51
+ Headers::ACCEPT => ACCEPT,
52
+ Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}"
53
+ ).post(url, body: body),
54
+ data: data
55
+ )
56
+ end
39
57
  end
40
58
 
41
59
  private
@@ -6,6 +6,8 @@ require 'trailer_vote/api/place'
6
6
  module TrailerVote
7
7
  module Api
8
8
  class Configuration
9
+
10
+ # @return [TrailerVote::Api::Place::Find] the root place attached to the credentials
9
11
  def place
10
12
  Place::Find.new(configuration: self)
11
13
  end
@@ -25,10 +27,18 @@ module TrailerVote
25
27
  self.result = result
26
28
  end
27
29
 
30
+ # @return [TrailerVote::Api::Configuration, TrailerVote::Api::Place::Create]
31
+ def back
32
+ backtrack = result
33
+ backtrack = result.back while backtrack&.is_a?(self.class)
34
+ backtrack || configuration
35
+ end
36
+
28
37
  def call(url: resolve_url)
29
- puts url
30
38
  return self if ok? || !url
31
- branch(resolve_client.headers(Headers::ACCEPT => ACCEPT).get(url))
39
+ guard_network_errors do
40
+ branch(resolve_client.headers(Headers::ACCEPT => ACCEPT).get(url))
41
+ end
32
42
  end
33
43
 
34
44
  def resolve_url
@@ -1,13 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'trailer_vote/media_types'
3
4
  require 'trailer_vote/api/configuration'
4
5
 
5
6
  module TrailerVote
6
7
  module Api
7
8
  class Configuration
9
+ # @return [TrailerVote::Api::Product] api to deal with products
8
10
  def product
9
11
  Product.new(configuration: self)
10
12
  end
13
+
14
+ alias products product
11
15
  end
12
16
 
13
17
  class Product
@@ -15,6 +19,10 @@ module TrailerVote
15
19
  self.configuration = configuration
16
20
  end
17
21
 
22
+ def back
23
+ configuration
24
+ end
25
+
18
26
  private
19
27
 
20
28
  attr_accessor :configuration
@@ -8,7 +8,7 @@ require 'trailer_vote/api/product/find'
8
8
  module TrailerVote
9
9
  module Api
10
10
  class Product
11
-
11
+ # @return [TrailerVote::Api::Product::Create] api to create a new product
12
12
  def create
13
13
  Create.new(configuration: configuration)
14
14
  end
@@ -26,15 +26,22 @@ module TrailerVote
26
26
  self.configuration = configuration
27
27
  end
28
28
 
29
+ # @return [TrailerVote::Api::Product] api to deal with products
30
+ def back
31
+ configuration.product
32
+ end
33
+
29
34
  def call(data:, url: resolve_url)
30
35
  body = encode(data)
31
- branch(
32
- resolve_client.headers(
33
- Headers::ACCEPT => ACCEPT,
34
- Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}"
35
- ).post(url, body: body),
36
- data: data
37
- )
36
+ guard_network_errors do
37
+ branch(
38
+ resolve_client.headers(
39
+ Headers::ACCEPT => ACCEPT,
40
+ Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}"
41
+ ).post(url, body: body),
42
+ data: data
43
+ )
44
+ end
38
45
  end
39
46
 
40
47
  private
@@ -6,7 +6,12 @@ require 'trailer_vote/api/product'
6
6
  module TrailerVote
7
7
  module Api
8
8
  class Product
9
-
9
+ # @param [TrailerVote::Api::Product::Find,
10
+ # TrailerVote::Api::Product::Create,
11
+ # TrailerVote::Api::Product::Update,
12
+ # TrailerVote::Api::Product::Lookup,
13
+ # NilClass] result the found product
14
+ # @return [TrailerVote::Api::Product::Find] api to deal with a found product
10
15
  def find(result: nil)
11
16
  Find.new(configuration: configuration, result: result)
12
17
  end
@@ -23,9 +28,22 @@ module TrailerVote
23
28
  self.result = result
24
29
  end
25
30
 
31
+ # @return [TrailerVote::Api::Product::Find,
32
+ # TrailerVote::Api::Product::Create,
33
+ # TrailerVote::Api::Product::Update,
34
+ # TrailerVote::Api::Product::Lookup,
35
+ # NilClass] return the api that yielded this
36
+ def back
37
+ backtrack = result
38
+ backtrack = result.back while backtrack&.is_a?(self.class)
39
+ backtrack
40
+ end
41
+
26
42
  def call(url: nil, data: nil)
27
43
  return self if ok? || !url
28
- branch(resolve_client.headers(Headers::ACCEPT => ACCEPT).get(url), data: data)
44
+ guard_network_errors do
45
+ branch(resolve_client.headers(Headers::ACCEPT => ACCEPT).get(url), data: data)
46
+ end
29
47
  end
30
48
 
31
49
  def data
@@ -7,11 +7,13 @@ module TrailerVote
7
7
  module Api
8
8
  class Product
9
9
  class Find
10
+ # @return [TrailerVote::Api::Product::Image] api to deal with a product's images
10
11
  def image
11
12
  Image.new(configuration: configuration, product: self)
12
13
  end
13
14
 
14
15
  alias product_image image
16
+ alias images image
15
17
  end
16
18
 
17
19
  class Image
@@ -20,6 +22,11 @@ module TrailerVote
20
22
  self.product = product
21
23
  end
22
24
 
25
+ # @return [TrailerVote::Api::Product::Find] api to deal with a found product
26
+ def back
27
+ product
28
+ end
29
+
23
30
  private
24
31
 
25
32
  attr_accessor :configuration, :product
@@ -9,6 +9,8 @@ module TrailerVote
9
9
  module Api
10
10
  class Product
11
11
  class Image
12
+
13
+ # @return [TrailerVote::Api::Product::Image::Create] the api to create an image for the current +product+
12
14
  def create
13
15
  Create.new(configuration: configuration, product: product)
14
16
  end
@@ -27,15 +29,30 @@ module TrailerVote
27
29
  self.product = product
28
30
  end
29
31
 
32
+ # @return [TrailerVote::Api::Product::Image] the api to deal with the current +product+ images
33
+ def back
34
+ product.image
35
+ end
36
+
37
+ # Create the image
38
+ #
39
+ # @see https://www.rubydoc.info/gems/trailer_vote-media_types/TrailerVote/MediaTypes/ProductImage TrailerVote::MediaTypes::ProductImage
40
+ #
41
+ # @param [String] url (#resolve_url result) the url to post to
42
+ # @param [Hash] data the image data
43
+ #
44
+ # @return [TrailerVote::Api::Product::Image::Find]
30
45
  def call(data:, url: resolve_url)
31
46
  body = encode(data)
32
- branch(
33
- resolve_client.headers(
34
- Headers::ACCEPT => ACCEPT,
35
- Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}"
36
- ).post(url, body: body),
37
- data: data
38
- )
47
+ guard_network_errors do
48
+ branch(
49
+ resolve_client.headers(
50
+ Headers::ACCEPT => ACCEPT,
51
+ Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}"
52
+ ).post(url, body: body),
53
+ data: data
54
+ )
55
+ end
39
56
  end
40
57
 
41
58
  private
@@ -7,6 +7,12 @@ module TrailerVote
7
7
  module Api
8
8
  class Product
9
9
  class Image
10
+
11
+ # @param [TrailerVote::Api::Product::Image::Find,
12
+ # TrailerVote::Api::Product::Image::Create,
13
+ # TrailerVote::Api::Product::Image::Urls::Traverse,
14
+ # NilClass] result the found image, or nil
15
+ # @return [TrailerVote::Api::Product::Image::Find] the api to deal with the found image
10
16
  def find(result: nil)
11
17
  Find.new(configuration: configuration, result: result)
12
18
  end
@@ -24,6 +30,15 @@ module TrailerVote
24
30
  self.result = result
25
31
  end
26
32
 
33
+ # @return [TrailerVote::Api::Product::Image::Create,
34
+ # TrailerVote::Api::Product::Image::Urls::Traverse,
35
+ # NilClass] return the api that yielded this
36
+ def back
37
+ backtrack = result
38
+ backtrack = result.back while backtrack&.is_a?(self.class)
39
+ backtrack
40
+ end
41
+
27
42
  def call(url: nil)
28
43
  return self if ok? || !url
29
44
  branch(resolve_client.headers(Headers::ACCEPT => ACCEPT).get(url))
@@ -7,6 +7,8 @@ module TrailerVote
7
7
  module Api
8
8
  class Product
9
9
  class Image
10
+
11
+ # @return [TrailerVote::Api::Product::Image::Urls] the api to get the image urls for the current product
10
12
  def urls
11
13
  Urls.new(configuration: configuration, product: product)
12
14
  end
@@ -25,9 +27,16 @@ module TrailerVote
25
27
  self.result = result
26
28
  end
27
29
 
30
+ # @return [TrailerVote::Api::Product::Image] the api to deal with a product's images
31
+ def back
32
+ product.image
33
+ end
34
+
28
35
  def call(url: resolve_url)
29
36
  return self if ok? || !url
30
- branch(resolve_client.headers(Headers::ACCEPT => ACCEPT).get(url))
37
+ guard_network_errors do
38
+ branch(resolve_client.headers(Headers::ACCEPT => ACCEPT).get(url))
39
+ end
31
40
  end
32
41
 
33
42
  def data
@@ -7,8 +7,17 @@ require 'trailer_vote/api/product/find'
7
7
 
8
8
  module TrailerVote
9
9
  module Api
10
+
11
+ class Configuration
12
+ # @return [TrailerVote::Api::Product::Lookup] api to deal with looking up a product
13
+ def product_lookup
14
+ product.lookup
15
+ end
16
+ end
17
+
10
18
  class Product
11
19
 
20
+ # @return [TrailerVote::Api::Product::Lookup] api to deal with looking up a product
12
21
  def lookup
13
22
  Lookup.new(configuration: configuration)
14
23
  end
@@ -49,15 +58,22 @@ module TrailerVote
49
58
  self.configuration = configuration
50
59
  end
51
60
 
61
+ # @return [TrailerVote::Api::Product] api to deal with products
62
+ def back
63
+ configuration.product
64
+ end
65
+
52
66
  def call(data:, url: resolve_url)
53
67
  body = encode(data)
54
- branch(
55
- resolve_client.headers(
56
- Headers::ACCEPT => ACCEPT,
57
- Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}"
58
- ).post(url, body: body),
59
- data: data
60
- )
68
+ guard_network_errors do
69
+ branch(
70
+ resolve_client.headers(
71
+ Headers::ACCEPT => ACCEPT,
72
+ Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}"
73
+ ).post(url, body: body),
74
+ data: data
75
+ )
76
+ end
61
77
  end
62
78
 
63
79
  private
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'trailer_vote/media_types'
4
+
3
5
  require 'trailer_vote/api/product'
4
6
  require 'trailer_vote/api/product/find'
5
7
 
@@ -7,11 +9,13 @@ module TrailerVote
7
9
  module Api
8
10
  class Product
9
11
  class Find
12
+ # @return [TrailerVote::Api::Place] api to deal with a product's places
10
13
  def place
11
14
  Place.new(configuration: configuration, product: self)
12
15
  end
13
16
 
14
17
  alias product_place place
18
+ alias places place
15
19
  end
16
20
 
17
21
  class Place
@@ -20,6 +24,11 @@ module TrailerVote
20
24
  self.product = product
21
25
  end
22
26
 
27
+ # @return [TrailerVote::Api::Place::Find] api to deal with a found product
28
+ def back
29
+ product
30
+ end
31
+
23
32
  private
24
33
 
25
34
  attr_accessor :configuration, :product
@@ -9,6 +9,8 @@ module TrailerVote
9
9
  module Api
10
10
  class Product
11
11
  class Place
12
+
13
+ # @return [TrailerVote::Api::Product::Place::Link] api to link a place to the current +product+
12
14
  def link
13
15
  Link.new(configuration: configuration, product: product)
14
16
  end
@@ -27,19 +29,28 @@ module TrailerVote
27
29
  self.product = product
28
30
  end
29
31
 
32
+ # @return [TrailerVote::Api::Product::Place] the api to deal with the product places
33
+ def back
34
+ product.place
35
+ end
36
+
30
37
  def call(data:, url: resolve_url)
31
38
  body = encode(data)
32
- branch(
33
- resolve_client.headers(
34
- Headers::ACCEPT => ACCEPT,
35
- Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}"
36
- ).post(url, body: body),
37
- data: data
38
- )
39
+ guard_network_errors do
40
+ branch(
41
+ resolve_client.headers(
42
+ Headers::ACCEPT => ACCEPT,
43
+ Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}"
44
+ ).post(url, body: body),
45
+ data: data
46
+ )
47
+ end
39
48
  end
40
49
 
41
50
  private
42
51
 
52
+ attr_accessor :product
53
+
43
54
  def resolve_url
44
55
  product.links.places
45
56
  end
@@ -9,6 +9,7 @@ module TrailerVote
9
9
  module Api
10
10
  class Product
11
11
  class Find
12
+ # @return [TrailerVote::Api::Product::Update] api to deal with a updating a found product
12
13
  def update
13
14
  Update.new(configuration: configuration, product: self)
14
15
  end
@@ -28,16 +29,23 @@ module TrailerVote
28
29
  self.product = product
29
30
  end
30
31
 
32
+ # @return [TrailerVote::Api::Product::Find] api to deal with a found product
33
+ def back
34
+ product
35
+ end
36
+
31
37
  def call(data:, url: resolve_url, etag: resolve_etag)
32
38
  body = encode(data)
33
- branch(
34
- resolve_client.headers(
35
- Headers::ACCEPT => ACCEPT,
36
- Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}",
37
- Headers::IF_MATCH => etag
38
- ).put(url, body: body),
39
- data: data
40
- )
39
+ guard_network_errors do
40
+ branch(
41
+ resolve_client.headers(
42
+ Headers::ACCEPT => ACCEPT,
43
+ Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}",
44
+ Headers::IF_MATCH => etag
45
+ ).put(url, body: body),
46
+ data: data
47
+ )
48
+ end
41
49
  end
42
50
 
43
51
  private
@@ -7,11 +7,13 @@ module TrailerVote
7
7
  module Api
8
8
  class Product
9
9
  class Find
10
+ # @return [TrailerVote::Api::Product::Video] api to deal with a product's videos
10
11
  def video
11
12
  Video.new(configuration: configuration, product: self)
12
13
  end
13
14
 
14
15
  alias product_video video
16
+ alias videos video
15
17
  end
16
18
 
17
19
  class Video
@@ -20,6 +22,11 @@ module TrailerVote
20
22
  self.product = product
21
23
  end
22
24
 
25
+ # @return [TrailerVote::Api::Product::Find] api to deal with a found product
26
+ def back
27
+ product
28
+ end
29
+
23
30
  private
24
31
 
25
32
  attr_accessor :configuration, :product
@@ -27,15 +27,29 @@ module TrailerVote
27
27
  self.product = product
28
28
  end
29
29
 
30
+ def back
31
+ product.video
32
+ end
33
+
34
+ # Create the image
35
+ #
36
+ # @see https://www.rubydoc.info/gems/trailer_vote-media_types/TrailerVote/MediaTypes/ProductImage TrailerVote::MediaTypes::ProductImage
37
+ #
38
+ # @param [String] url (#resolve_url result) the url to post to
39
+ # @param [Hash] data the image data
40
+ #
41
+ # @return [TrailerVote::Api::Product::Video::Find]
30
42
  def call(data:, url: resolve_url)
31
43
  body = encode(data)
32
- branch(
33
- resolve_client.headers(
34
- Headers::ACCEPT => ACCEPT,
35
- Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}"
36
- ).post(url, body: body),
37
- data: data
38
- )
44
+ guard_network_errors do
45
+ branch(
46
+ resolve_client.headers(
47
+ Headers::ACCEPT => ACCEPT,
48
+ Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}"
49
+ ).post(url, body: body),
50
+ data: data
51
+ )
52
+ end
39
53
  end
40
54
 
41
55
  private
@@ -7,6 +7,12 @@ module TrailerVote
7
7
  module Api
8
8
  class Product
9
9
  class Video
10
+
11
+ # @param [TrailerVote::Api::Product::Video::Find,
12
+ # TrailerVote::Api::Product::Video::Create,
13
+ # TrailerVote::Api::Product::Video::Urls::Traverse,
14
+ # NilClass] result the found video, or nil
15
+ # @return [TrailerVote::Api::Product::Video::Find] the api to deal with the found video
10
16
  def find(result: nil)
11
17
  Find.new(configuration: configuration, result: result)
12
18
  end
@@ -24,9 +30,20 @@ module TrailerVote
24
30
  self.result = result
25
31
  end
26
32
 
33
+ # @return [TrailerVote::Api::Product::Video::Create,
34
+ # TrailerVote::Api::Product::Video::Urls::Traverse,
35
+ # NilClass] return the api that yielded this
36
+ def back
37
+ backtrack = result
38
+ backtrack = result.back while backtrack&.is_a?(self.class)
39
+ backtrack
40
+ end
41
+
27
42
  def call(url: nil)
28
43
  return self if ok? || !url
29
- branch(resolve_client.headers(Headers::ACCEPT => ACCEPT).get(url))
44
+ guard_network_errors do
45
+ branch(resolve_client.headers(Headers::ACCEPT => ACCEPT).get(url))
46
+ end
30
47
  end
31
48
 
32
49
  def data
@@ -7,6 +7,8 @@ module TrailerVote
7
7
  module Api
8
8
  class Product
9
9
  class Video
10
+
11
+ # @return [TrailerVote::Api::Product::Video::Urls] the api to get the video urls for the current product
10
12
  def urls
11
13
  Urls.new(configuration: configuration, product: product)
12
14
  end
@@ -25,9 +27,16 @@ module TrailerVote
25
27
  self.result = result
26
28
  end
27
29
 
30
+ # @return [TrailerVote::Api::Product::Video] the api to deal with a product's videos
31
+ def back
32
+ product.video
33
+ end
34
+
28
35
  def call(url: resolve_url)
29
36
  return self if ok? || !url
30
- branch(resolve_client.headers(Headers::ACCEPT => ACCEPT).get(url))
37
+ guard_network_errors do
38
+ branch(resolve_client.headers(Headers::ACCEPT => ACCEPT).get(url))
39
+ end
31
40
  end
32
41
 
33
42
  def data
@@ -19,6 +19,8 @@ module TrailerVote
19
19
 
20
20
  def [](key)
21
21
  registry.fetch(key) { registry.fetch(registry_shortcuts.fetch(key)) }
22
+ rescue KeyError
23
+ raise UnknownMediaType, format('Unknown media type %<type>s', type: key)
22
24
  end
23
25
 
24
26
  def []=(key, value)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module TrailerVote
4
4
  module Api
5
- VERSION = '0.6.1'
5
+ VERSION = '0.7.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trailer_vote-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Derk-Jan Karrenbeld
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-07 00:00:00.000000000 Z
11
+ date: 2018-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http
@@ -204,6 +204,7 @@ files:
204
204
  - lib/trailer_vote/api/composable/get.rb
205
205
  - lib/trailer_vote/api/configuration.rb
206
206
  - lib/trailer_vote/api/errors.rb
207
+ - lib/trailer_vote/api/fallback_content_types.rb
207
208
  - lib/trailer_vote/api/links.rb
208
209
  - lib/trailer_vote/api/place.rb
209
210
  - lib/trailer_vote/api/place/create.rb