trailer_vote-api 0.6.1 → 0.7.0

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