trailer_vote-api 0.5.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 +7 -0
- data/.gitignore +13 -0
- data/.rubocop.yml +29 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +77 -0
- data/README.md +184 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/trailer_vote/api/autoload.rb +15 -0
- data/lib/trailer_vote/api/composable/common.rb +61 -0
- data/lib/trailer_vote/api/composable/get.rb +54 -0
- data/lib/trailer_vote/api/configuration.rb +69 -0
- data/lib/trailer_vote/api/errors.rb +62 -0
- data/lib/trailer_vote/api/links.rb +51 -0
- data/lib/trailer_vote/api/place/create.rb +65 -0
- data/lib/trailer_vote/api/place/find.rb +49 -0
- data/lib/trailer_vote/api/place.rb +19 -0
- data/lib/trailer_vote/api/product/create.rb +56 -0
- data/lib/trailer_vote/api/product/find.rb +37 -0
- data/lib/trailer_vote/api/product/image/create.rb +66 -0
- data/lib/trailer_vote/api/product/image/find.rb +39 -0
- data/lib/trailer_vote/api/product/image/urls.rb +57 -0
- data/lib/trailer_vote/api/product/image.rb +31 -0
- data/lib/trailer_vote/api/product/lookup.rb +81 -0
- data/lib/trailer_vote/api/product/place/link.rb +64 -0
- data/lib/trailer_vote/api/product/place.rb +31 -0
- data/lib/trailer_vote/api/product/update.rb +72 -0
- data/lib/trailer_vote/api/product/video/create.rb +66 -0
- data/lib/trailer_vote/api/product/video/find.rb +39 -0
- data/lib/trailer_vote/api/product/video/urls.rb +57 -0
- data/lib/trailer_vote/api/product/video.rb +31 -0
- data/lib/trailer_vote/api/product.rb +25 -0
- data/lib/trailer_vote/api/type_registry.rb +93 -0
- data/lib/trailer_vote/api/version.rb +7 -0
- data/lib/trailer_vote/api.rb +42 -0
- data/trailer_vote-api.gemspec +35 -0
- metadata +234 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8c83428f8cdf27b0b7be61b7f1b39f67cae7a721
|
4
|
+
data.tar.gz: 1d14fcbd1e8d7759a19a7feb6d05ed1b5f5f8d7f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e74475a1590286c488e70f83338f43fe8c001e81fef23fab18dd66ddfcdcaa5774ab3d6a0db8a69ca84c9e2dce92688d622f6d40ec73560390aa45bd1d984f37
|
7
|
+
data.tar.gz: a2028156d2b770043cebca99a536a128161dfb60e18565ea36994ca770a83f51b54397bc1a4aeeb781bfa455c595dd32a7f84118b001a623b7b1e4882d83eceb
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
AllCops:
|
2
|
+
Include:
|
3
|
+
- '**/Rakefile'
|
4
|
+
- 'lib/**/*.rb'
|
5
|
+
Exclude:
|
6
|
+
- 'Gemfile'
|
7
|
+
- 'bin/**/*'
|
8
|
+
TargetRubyVersion: 2.3
|
9
|
+
|
10
|
+
Layout/EmptyLinesAroundClassBody:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Layout/EndOfLine:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
Metrics/LineLength:
|
17
|
+
Max: 120
|
18
|
+
|
19
|
+
Metrics/MethodLength:
|
20
|
+
Max: 15
|
21
|
+
|
22
|
+
Style/Documentation:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Style/EmptyMethod:
|
26
|
+
EnforcedStyle: expanded
|
27
|
+
|
28
|
+
Style/IfUnlessModifier:
|
29
|
+
Enabled: false
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
trailer_vote-api (0.5.0)
|
5
|
+
http
|
6
|
+
oj (~> 3.6)
|
7
|
+
trailer_vote-media_types (~> 0.6.1)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
addressable (2.5.2)
|
13
|
+
public_suffix (>= 2.0.2, < 4.0)
|
14
|
+
ansi (1.5.0)
|
15
|
+
awesome_print (1.8.0)
|
16
|
+
builder (3.2.3)
|
17
|
+
crack (0.4.3)
|
18
|
+
safe_yaml (~> 1.0.0)
|
19
|
+
docile (1.3.1)
|
20
|
+
domain_name (0.5.20180417)
|
21
|
+
unf (>= 0.0.5, < 1.0.0)
|
22
|
+
hashdiff (0.3.7)
|
23
|
+
http (3.3.0)
|
24
|
+
addressable (~> 2.3)
|
25
|
+
http-cookie (~> 1.0)
|
26
|
+
http-form_data (~> 2.0)
|
27
|
+
http_parser.rb (~> 0.6.0)
|
28
|
+
http-cookie (1.0.3)
|
29
|
+
domain_name (~> 0.5)
|
30
|
+
http-form_data (2.1.1)
|
31
|
+
http_parser.rb (0.6.0)
|
32
|
+
json (2.1.0)
|
33
|
+
media_types (0.6.0)
|
34
|
+
minitest (5.11.3)
|
35
|
+
minitest-ci (3.4.0)
|
36
|
+
minitest (>= 5.0.6)
|
37
|
+
minitest-reporters (1.3.5)
|
38
|
+
ansi
|
39
|
+
builder
|
40
|
+
minitest (>= 5.0)
|
41
|
+
ruby-progressbar
|
42
|
+
oj (3.6.11)
|
43
|
+
public_suffix (3.0.3)
|
44
|
+
rake (10.5.0)
|
45
|
+
ruby-progressbar (1.10.0)
|
46
|
+
safe_yaml (1.0.4)
|
47
|
+
simplecov (0.16.1)
|
48
|
+
docile (~> 1.1)
|
49
|
+
json (>= 1.8, < 3)
|
50
|
+
simplecov-html (~> 0.10.0)
|
51
|
+
simplecov-html (0.10.2)
|
52
|
+
trailer_vote-media_types (0.6.1)
|
53
|
+
media_types (~> 0.6.0)
|
54
|
+
unf (0.1.4)
|
55
|
+
unf_ext
|
56
|
+
unf_ext (0.0.7.5-x64-mingw32)
|
57
|
+
webmock (3.4.2)
|
58
|
+
addressable (>= 2.3.6)
|
59
|
+
crack (>= 0.3.2)
|
60
|
+
hashdiff
|
61
|
+
|
62
|
+
PLATFORMS
|
63
|
+
x64-mingw32
|
64
|
+
|
65
|
+
DEPENDENCIES
|
66
|
+
awesome_print (~> 1.8)
|
67
|
+
bundler (~> 1.16)
|
68
|
+
minitest (~> 5.11)
|
69
|
+
minitest-ci (~> 3.4)
|
70
|
+
minitest-reporters (~> 1.3)
|
71
|
+
rake (~> 10.5)
|
72
|
+
simplecov (~> 0.16)
|
73
|
+
trailer_vote-api!
|
74
|
+
webmock (~> 3.4)
|
75
|
+
|
76
|
+
BUNDLED WITH
|
77
|
+
1.16.5
|
data/README.md
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
# TrailerVote::Api
|
2
|
+
|
3
|
+
The TrailerVote Api gem is the official interface to communicate with the TrailerVote product service. It allows you to
|
4
|
+
keep your code simple and not deal with the HTTP suite.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'trailer_vote-api'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install trailer_vote-api
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
By default only the configuration api calls are available and you need to require which ever calls you want to make. A call always loads it dependencies. If you want to load everything you can `require 'trailer_vote/api/autoload'`.
|
25
|
+
|
26
|
+
In order to call any of the APIs, you need to configure the api first:
|
27
|
+
```Ruby
|
28
|
+
key = '<TrailerVote API Client key>'
|
29
|
+
secret = '<TrailerVote API Client secret>'
|
30
|
+
url = '<TrailerVote API environment url>'
|
31
|
+
|
32
|
+
configuration = TrailerVote::Api.configure(url: url, key: key, secret: secret)
|
33
|
+
# => TrailerVote::Api::Configuration
|
34
|
+
```
|
35
|
+
|
36
|
+
The TrailerVote API Client for Ruby uses a lazy chainable API and does **NOT** make any requests until it's absolutely necessary.
|
37
|
+
It is recommended that you re-use the configuration instance, as well as other results, in order to reduce network requests.
|
38
|
+
|
39
|
+
The common interface is:
|
40
|
+
- `#call(*args)`: Make the call and return an object that has results
|
41
|
+
- `#data`: Return the inner data (`#to_h`) without the wrapping key
|
42
|
+
- `#to_i`: Return the HTTP status code
|
43
|
+
- `#to_h`: Return the response (`#call`), parsed (if parsable, like JSON) and validated (according to the media type)
|
44
|
+
- `#etag`: Return the HTTP ETag header value, if any
|
45
|
+
- `#links`: Return the HTTP Link header / inner `_links` as `Links` object
|
46
|
+
|
47
|
+
Unless data is being posted, `#call` is not necessary:
|
48
|
+
|
49
|
+
```Ruby
|
50
|
+
configuration = TrailerVote::Api.configure(url: url, key: key, secret: secret)
|
51
|
+
configuration.links
|
52
|
+
# => TrailerVote::Api::Links # Makes the HTTP call if necessary
|
53
|
+
```
|
54
|
+
|
55
|
+
In case of an error, a `TrailerVote::Api::Error` is raised, with subclasses defining what went wrong. If the API gives back an error (HTTP status `(400..599)`), the error is parsed and turned into a `ErrorsResponse < Error` error.
|
56
|
+
|
57
|
+
## Interface
|
58
|
+
|
59
|
+
Only the call available to the current link in the chain, if `required` are available on each object.
|
60
|
+
|
61
|
+
### `configuration`
|
62
|
+
You don't need to require this, it's always loaded.
|
63
|
+
|
64
|
+
```Ruby
|
65
|
+
require 'trailer_vote/api/configuration'
|
66
|
+
|
67
|
+
configuration = TrailerVote::Api.configure(key: '', secret: '', url: '')
|
68
|
+
# => TrailerVote::Api::Configuration
|
69
|
+
```
|
70
|
+
|
71
|
+
### `configuration.product.lookup`
|
72
|
+
Used to lookup products by authority:identifier pairs. If found, returns the actual `Product::Find`.
|
73
|
+
|
74
|
+
The `data` argument is wrapped in `{ product_identifiers: data }` and then needs to match `application/vnd.trailervote.product.lookup.v1+json`
|
75
|
+
|
76
|
+
```Ruby
|
77
|
+
require 'trailer_vote/api/product/lookup'
|
78
|
+
|
79
|
+
lookup = configuration.product.lookup
|
80
|
+
# => TrailerVote::Api::Product::Lookup
|
81
|
+
|
82
|
+
lookup.call(data: [{ authority: 'imdb', identifier: 'tt01010101' }, { authority: 'tmdb', identifier: '12345678' }])
|
83
|
+
# => TrailerVote::Api::Product::Find
|
84
|
+
```
|
85
|
+
|
86
|
+
### `configuration.product.update`
|
87
|
+
Used to update a product. Is only available on `Product::Find`, because we require updates to be non-stale, that is you need to ensure that you are the last one updating the product, so merging data can be done correctly.
|
88
|
+
|
89
|
+
The `data` argument is wrapped in `{ product: data }` and then needs to match `application/vnd.trailervote.product.v2+json`
|
90
|
+
|
91
|
+
```Ruby
|
92
|
+
require 'trailer_vote/api/product/lookup'
|
93
|
+
require 'trailer_vote/api/product/update'
|
94
|
+
|
95
|
+
lookup = configuration.product.lookup
|
96
|
+
product = lookup.call(data: [{ authority: 'imdb', identifier: 'tt01010101' }])
|
97
|
+
# => TrailerVote::Api::Product::Find
|
98
|
+
|
99
|
+
current_product_data = product.data
|
100
|
+
next_product_data = make_changes_to_product_data(current_product_data)
|
101
|
+
product.update.call(data: next_product_data)
|
102
|
+
# => TrailerVote::Api::Product::Find
|
103
|
+
```
|
104
|
+
|
105
|
+
If the update call fails with a `409 Conflict` or `412 Precondition Failed`, it means the product was updated in the meanwhile. You want to fetch the product again by doing another lookup and then running your strategy again.
|
106
|
+
|
107
|
+
### `configuration.product.create`
|
108
|
+
Creating a product is similar to looking up a product. If successful, returns an actual `Product::Find`.
|
109
|
+
|
110
|
+
The `data` argument is wrapped in `{ product: data }` and then needs to match `application/vnd.trailervote.product.v2.create+json`
|
111
|
+
|
112
|
+
```Ruby
|
113
|
+
require 'trailer_vote/api/product/create'
|
114
|
+
|
115
|
+
configuration.product.create(data: { title: 'My product', ... })
|
116
|
+
# => TrailerVote::Api::Product::Find
|
117
|
+
```
|
118
|
+
|
119
|
+
### `configuration.product.<>.video.create`
|
120
|
+
To attach an video to a product, you first need to find the product. This can be done by:
|
121
|
+
- `configuration.product.create`: creating a new product
|
122
|
+
- `configuration.product.lookup`: looking up an existing product
|
123
|
+
|
124
|
+
Once you have it, the video operations are available on the result.
|
125
|
+
|
126
|
+
The `data` argument is wrapped in `{ product_video: data }` and then needs to match `application/vnd.trailervote.product.video.v1.create+json`
|
127
|
+
|
128
|
+
```Ruby
|
129
|
+
require 'trailer_vote/api/product/lookup'
|
130
|
+
require 'trailer_vote/api/product/video/create'
|
131
|
+
|
132
|
+
product = configuration.product.lookup.call(data: [{ authority: 'imdb', identifier: 'tt01010101' }])
|
133
|
+
# => TrailerVote::Api::Product::Find
|
134
|
+
|
135
|
+
product.video.create(data: { source_url: '', ... })
|
136
|
+
# => TrailerVote::Api::Product::Video::Find
|
137
|
+
```
|
138
|
+
|
139
|
+
You can see in this example the `product` result is cached in a variable, so that if you want to create *many* videos, the product isn't looked up each call.
|
140
|
+
|
141
|
+
### `configuration.product.<>.image.urls`
|
142
|
+
Gets all the image urls for a product
|
143
|
+
|
144
|
+
### `configuration.product.<>.video.create`
|
145
|
+
To attach an video to a product, you first need to find the product. This can be done by:
|
146
|
+
- `configuration.product.create`: creating a new product
|
147
|
+
- `configuration.product.lookup`: looking up an existing product
|
148
|
+
|
149
|
+
Once you have it, the video operations are available on the result.
|
150
|
+
|
151
|
+
The `data` argument is wrapped in `{ product_video: data }` and then needs to match `application/vnd.trailervote.product.video.v1.create+json`
|
152
|
+
|
153
|
+
```Ruby
|
154
|
+
require 'trailer_vote/api/product/lookup'
|
155
|
+
require 'trailer_vote/api/product/video/create'
|
156
|
+
|
157
|
+
product = configuration.product.lookup.call(data: [{ authority: 'imdb', identifier: 'tt01010101' }])
|
158
|
+
# => TrailerVote::Api::Product::Find
|
159
|
+
|
160
|
+
product.video.create(data: { source_url: '', ... })
|
161
|
+
# => TrailerVote::Api::Product::Video::Find
|
162
|
+
```
|
163
|
+
|
164
|
+
You can see in this example the `product` result is cached in a variable, so that if you want to create *many* videos, the product isn't looked up each call.
|
165
|
+
|
166
|
+
### `configuration.product.<>.video.urls`
|
167
|
+
Gets all the image urls for a product
|
168
|
+
|
169
|
+
### `configuration.product.<>.place.link`
|
170
|
+
Links a place to a product
|
171
|
+
|
172
|
+
## Development
|
173
|
+
|
174
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can
|
175
|
+
also run `bin/console` for an interactive prompt that will allow you to experiment.
|
176
|
+
|
177
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the
|
178
|
+
version number in `version.rb`, and then run `bundle update trailer_vote-api` in any repository that depends on
|
179
|
+
this gem. If you have push rights, you may call `bundle exec rake release` to create a new git tag, push
|
180
|
+
git commits and tags, and push the `.gem` file to the rubygems gem server.
|
181
|
+
|
182
|
+
## Contributing
|
183
|
+
|
184
|
+
Bug reports and pull requests are welcome on GitHub at [TrailerVote/trailervote-api-clients](https://github.com/trailervote/trailervote-api-clients)
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'trailer_vote/api'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'trailer_vote/api'
|
4
|
+
require 'trailer_vote/api/place'
|
5
|
+
require 'trailer_vote/api/place/create'
|
6
|
+
require 'trailer_vote/api/place/find'
|
7
|
+
require 'trailer_vote/api/product'
|
8
|
+
require 'trailer_vote/api/product/create'
|
9
|
+
require 'trailer_vote/api/product/find'
|
10
|
+
require 'trailer_vote/api/product/lookup'
|
11
|
+
require 'trailer_vote/api/product/image/create'
|
12
|
+
require 'trailer_vote/api/product/image/find'
|
13
|
+
require 'trailer_vote/api/product/place/link'
|
14
|
+
require 'trailer_vote/api/product/video/create'
|
15
|
+
require 'trailer_vote/api/product/video/find'
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TrailerVote
|
4
|
+
module Api
|
5
|
+
module Composable
|
6
|
+
module Common
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
base.class_eval do
|
10
|
+
private
|
11
|
+
|
12
|
+
attr_accessor :configuration
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(**_opts)
|
17
|
+
raise format('Missing implementation of #args in %<name>s', self.class.name)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def resolve_client
|
23
|
+
configuration.client
|
24
|
+
end
|
25
|
+
|
26
|
+
def forward_klazz
|
27
|
+
self.class
|
28
|
+
end
|
29
|
+
|
30
|
+
def redirect_klazz
|
31
|
+
self.class
|
32
|
+
end
|
33
|
+
|
34
|
+
def branch(result, data: nil)
|
35
|
+
raise_on_error(result)
|
36
|
+
forward(result, data: data) || redirect(result)
|
37
|
+
end
|
38
|
+
|
39
|
+
def raise_on_error(result)
|
40
|
+
return unless result.status.client_error? || result.status.server_error?
|
41
|
+
TrailerVote::Api.raise_error result
|
42
|
+
end
|
43
|
+
|
44
|
+
def forward(result, data:)
|
45
|
+
return unless [307, 308].include?(result.status)
|
46
|
+
forward_klazz.new(configuration: configuration)
|
47
|
+
.call(data: data, url: redirected_url(result))
|
48
|
+
end
|
49
|
+
|
50
|
+
def redirect(result)
|
51
|
+
redirect_klazz.new(configuration: configuration, result: result)
|
52
|
+
.call(url: redirected_url(result))
|
53
|
+
end
|
54
|
+
|
55
|
+
def redirected_url(result)
|
56
|
+
result['Location'] || result['Content-Location']
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'trailer_vote/api/composable/common'
|
4
|
+
require 'trailer_vote/api/links'
|
5
|
+
|
6
|
+
module TrailerVote
|
7
|
+
module Api
|
8
|
+
module Composable
|
9
|
+
module Get
|
10
|
+
def self.included(base)
|
11
|
+
base.include Common
|
12
|
+
base.class_eval do
|
13
|
+
attr_accessor :result
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def links
|
18
|
+
# TODO: or headers
|
19
|
+
@links ||= Links.new(data['_links'])
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_h
|
23
|
+
@to_h ||= TrailerVote::Api.decode(call.result)
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_i
|
27
|
+
call.result.status
|
28
|
+
end
|
29
|
+
|
30
|
+
def etag
|
31
|
+
call.result[Headers::ETAG]
|
32
|
+
end
|
33
|
+
|
34
|
+
def data
|
35
|
+
raise format('Missing implementation of #data in %<name>s', self.class.name)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def ok?
|
41
|
+
result&.status == 200
|
42
|
+
end
|
43
|
+
|
44
|
+
def redirecting?
|
45
|
+
[301, 302, 303, 307, 308].include?(result&.status)
|
46
|
+
end
|
47
|
+
|
48
|
+
def redirect_to
|
49
|
+
self.class
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'trailer_vote/api/composable/get'
|
4
|
+
|
5
|
+
module TrailerVote
|
6
|
+
module Api
|
7
|
+
|
8
|
+
module_function
|
9
|
+
|
10
|
+
def configure(url:, key:, secret:)
|
11
|
+
client = Api.default_client(key, secret)
|
12
|
+
Configuration.new(client: client, url: url)
|
13
|
+
end
|
14
|
+
|
15
|
+
class Configuration
|
16
|
+
include Composable::Get
|
17
|
+
|
18
|
+
SUCCESS = MediaTypes::Configuration.to_constructable.version(2)
|
19
|
+
FAILURE = MediaTypes::Errors.to_constructable.version(1)
|
20
|
+
|
21
|
+
ACCEPT = [SUCCESS.to_s, FAILURE.to_s(0.1)].join(', ').freeze
|
22
|
+
|
23
|
+
attr_accessor :client
|
24
|
+
|
25
|
+
def initialize(client:, url: nil, result: nil)
|
26
|
+
self.client = client
|
27
|
+
self.result = result
|
28
|
+
self.url = url
|
29
|
+
end
|
30
|
+
|
31
|
+
def data
|
32
|
+
to_h['configuration']
|
33
|
+
end
|
34
|
+
|
35
|
+
def call(url: resolve_url)
|
36
|
+
return self if ok? || !url
|
37
|
+
merge(resolve_client.headers(Headers::ACCEPT => ACCEPT).get(url))
|
38
|
+
# TODO: result.raise_for_status
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_accessor :url
|
44
|
+
|
45
|
+
def ok?
|
46
|
+
result&.status == 200
|
47
|
+
end
|
48
|
+
|
49
|
+
def redirecting?(result)
|
50
|
+
[301, 302, 303, 307, 308].include?(result.status)
|
51
|
+
end
|
52
|
+
|
53
|
+
alias resolve_url url
|
54
|
+
alias resolve_client client
|
55
|
+
|
56
|
+
def merge(result)
|
57
|
+
raise_on_error(result)
|
58
|
+
self.result = redirecting?(result) ? redirect(result).result : result
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
def redirect(result)
|
63
|
+
call(url: result['Location'] || result['Content-Location'])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
TrailerVote::MediaTypes::Configuration.register
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TrailerVote
|
4
|
+
module Api
|
5
|
+
|
6
|
+
class Error < RuntimeError; end
|
7
|
+
|
8
|
+
class EncodeError < Error
|
9
|
+
def initialize(media_type:, source:)
|
10
|
+
super format(
|
11
|
+
'Failed to encode data for %<media_type>s. Reason: %<reason>s',
|
12
|
+
media_type: media_type,
|
13
|
+
reason: source.message
|
14
|
+
)
|
15
|
+
|
16
|
+
self.source = source
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_accessor :source
|
20
|
+
end
|
21
|
+
|
22
|
+
class DecodeError < Error
|
23
|
+
def initialize(media_type:, source:)
|
24
|
+
super format(
|
25
|
+
'Failed to decode data for %<media_type>s. Reason: %<reason>s',
|
26
|
+
media_type: media_type,
|
27
|
+
reason: source.message
|
28
|
+
)
|
29
|
+
|
30
|
+
self.source = source
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_accessor :source
|
34
|
+
end
|
35
|
+
|
36
|
+
class ErrorsResponse < Error
|
37
|
+
attr_accessor :result
|
38
|
+
|
39
|
+
def initialize(result)
|
40
|
+
self.result = result
|
41
|
+
super messages
|
42
|
+
end
|
43
|
+
|
44
|
+
def messages
|
45
|
+
result.status.reason
|
46
|
+
end
|
47
|
+
|
48
|
+
def data
|
49
|
+
@data ||= TrailerVote::Api.decode(result)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module_function
|
54
|
+
|
55
|
+
def raise_error(result)
|
56
|
+
raise ErrorsResponse, result
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
TrailerVote::MediaTypes::Errors.register
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module TrailerVote
|
2
|
+
module Api
|
3
|
+
class Links
|
4
|
+
def initialize(links)
|
5
|
+
self.links = links
|
6
|
+
end
|
7
|
+
|
8
|
+
def respond_to_missing?(method_name, include_private = false)
|
9
|
+
links.key?(String(method_name)) || super
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(method_name, *arguments)
|
13
|
+
if links.key?(String(method_name))
|
14
|
+
return call(String(method_name), *arguments)
|
15
|
+
end
|
16
|
+
|
17
|
+
if /[a-z_]+/.match? method_name
|
18
|
+
raise ArgumentError, format(
|
19
|
+
'Unknown link %<link>s. Available: %<links>s',
|
20
|
+
link: method_name,
|
21
|
+
links: links.keys
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
def [](key)
|
29
|
+
links.fetch(key)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_accessor :links
|
35
|
+
|
36
|
+
def call(link, **arguments)
|
37
|
+
if arguments.length.zero?
|
38
|
+
return self[link]['href']
|
39
|
+
end
|
40
|
+
|
41
|
+
fill_template(self[link]['href'], **arguments)
|
42
|
+
end
|
43
|
+
|
44
|
+
def fill_template(templated, **arguments)
|
45
|
+
arguments.each_with_object(templated) do |(variable, value), result|
|
46
|
+
result.gsub!("{#{variable}}", value)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|