tvdb_client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.rspec +2 -0
  4. data/.rubyversion +1 -0
  5. data/CHANGELOG.md +4 -0
  6. data/Gemfile +2 -0
  7. data/Guardfile +20 -0
  8. data/README.md +43 -0
  9. data/Rakefile +9 -0
  10. data/conf/settings.example.yml +7 -0
  11. data/lib/tvdb_client.rb +13 -0
  12. data/lib/tvdb_client/authorization.rb +46 -0
  13. data/lib/tvdb_client/client.rb +30 -0
  14. data/lib/tvdb_client/connection.rb +98 -0
  15. data/lib/tvdb_client/series.rb +54 -0
  16. data/lib/tvdb_client/series/base.rb +32 -0
  17. data/lib/tvdb_client/series/series_episodes.rb +18 -0
  18. data/lib/tvdb_client/series/series_filter.rb +21 -0
  19. data/lib/tvdb_client/series/series_images.rb +14 -0
  20. data/lib/tvdb_client/service/threading.rb +8 -0
  21. data/lib/tvdb_client/service/threading/threaded_request.rb +51 -0
  22. data/lib/tvdb_client/settings.rb +10 -0
  23. data/lib/tvdb_client/version.rb +3 -0
  24. data/spec/spec_helper.rb +53 -0
  25. data/spec/support/fake_TVDB.rb +118 -0
  26. data/spec/support/fixtures/responses/login.json +3 -0
  27. data/spec/support/fixtures/responses/refresh_token.json +3 -0
  28. data/spec/support/fixtures/responses/series.json +33 -0
  29. data/spec/support/fixtures/responses/series_episodes_page_1.json +56 -0
  30. data/spec/support/fixtures/responses/series_episodes_page_2.json +56 -0
  31. data/spec/support/fixtures/responses/series_episodes_page_n.json +41 -0
  32. data/spec/support/fixtures/responses/series_episodes_query_airedSeason.json +41 -0
  33. data/spec/support/fixtures/responses/series_episodes_query_params.json +12 -0
  34. data/spec/support/fixtures/responses/series_episodes_summary.json +26 -0
  35. data/spec/support/fixtures/responses/series_filter.json +5 -0
  36. data/spec/support/fixtures/responses/series_filter_params.json +27 -0
  37. data/spec/support/fixtures/responses/series_images.json +9 -0
  38. data/spec/support/fixtures/responses/series_images_query.json +35 -0
  39. data/spec/support/fixtures/responses/series_images_query_params.json +60 -0
  40. data/spec/support/fixtures/settings.example.yml +6 -0
  41. data/spec/support/shared_contexts/authentication.rb +13 -0
  42. data/spec/support/shared_contexts/connection.rb +20 -0
  43. data/spec/support/shared_contexts/credentials.rb +20 -0
  44. data/spec/support/shared_contexts/series_subclass.rb +19 -0
  45. data/spec/tvdb_client/authorization_spec.rb +69 -0
  46. data/spec/tvdb_client/client_spec.rb +45 -0
  47. data/spec/tvdb_client/connection_spec.rb +159 -0
  48. data/spec/tvdb_client/series/base_spec.rb +13 -0
  49. data/spec/tvdb_client/series/series_episodes_spec.rb +55 -0
  50. data/spec/tvdb_client/series/series_filter_spec.rb +45 -0
  51. data/spec/tvdb_client/series/series_images_spec.rb +43 -0
  52. data/spec/tvdb_client/series_spec.rb +69 -0
  53. data/spec/tvdb_client/service/threading/threaded_request_spec.rb +54 -0
  54. data/spec/tvdb_client/version_spec.rb +12 -0
  55. data/tasks/version_bump.rake +102 -0
  56. data/templates/changelog_entry_template.md.erb +3 -0
  57. data/templates/version.rb.erb +3 -0
  58. data/tvdb_client.gemspec +41 -0
  59. metadata +412 -0
@@ -0,0 +1,41 @@
1
+ {
2
+ "errors": null,
3
+ "links": {
4
+ "first": 1,
5
+ "last": 1,
6
+ "next": null,
7
+ "prev": null
8
+ },
9
+ "data": [
10
+ {
11
+ "absoluteNumber": "749",
12
+ "airedEpisodeNumber": 6,
13
+ "airedSeason": "15",
14
+ "dvdEpisodeNumber": null,
15
+ "dvdSeason": null,
16
+ "episodeName": "Jostling for the Junior Cup!",
17
+ "firstAired": "2012-08-02",
18
+ "id": 4359831,
19
+ "language": {
20
+ "episodeName": "en",
21
+ "overview": "en"
22
+ },
23
+ "overview": "Ash and his friends arrive in Lacunosa Town for the Junior Cup. The Cup begins with an exhibition match between Cynthia and Caitlyn. Iris and Georgia face off against each other but Iris’ newly caught Dragonite refuses to listen to Iris’ commands."
24
+ },
25
+ {
26
+ "absoluteNumber": null,
27
+ "airedEpisodeNumber": 6,
28
+ "airedSeason": "15",
29
+ "dvdEpisodeNumber": null,
30
+ "dvdSeason": null,
31
+ "episodeName": "Battling on thin ice!",
32
+ "firstAired": "2013-11-14",
33
+ "id": 4709580,
34
+ "language": {
35
+ "episodeName": "en",
36
+ "overview": "en"
37
+ },
38
+ "overview": "Ash challenges Viola to a rematch, now ready for Surskit's freezing strategy. "
39
+ }
40
+ ]
41
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "data": [
3
+ "airedSeason",
4
+ "airedEpisode",
5
+ "seriesId",
6
+ "imdbId",
7
+ "dvdSeason",
8
+ "dvdEpisode",
9
+ "absoluteNumber",
10
+ "page"
11
+ ]
12
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "data": {
3
+ "airedSeasons": [
4
+ "12",
5
+ "1",
6
+ "2",
7
+ "0",
8
+ "3",
9
+ "4",
10
+ "5",
11
+ "6",
12
+ "7",
13
+ "8",
14
+ "14",
15
+ "9",
16
+ "10",
17
+ "11",
18
+ "13",
19
+ "15",
20
+ "16"
21
+ ],
22
+ "airedEpisodes": "923",
23
+ "dvdSeasons": [],
24
+ "dvdEpisodes": "0"
25
+ }
26
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "data": {
3
+ "seriesName": "Pokémon"
4
+ }
5
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "data": {
3
+ "params": [
4
+ "id",
5
+ "seriesName",
6
+ "banner",
7
+ "status",
8
+ "runtime",
9
+ "lastUpdated",
10
+ "zap2itId",
11
+ "aliases",
12
+ "seriesId",
13
+ "actors",
14
+ "airsDayOfWeek",
15
+ "imdbId",
16
+ "added",
17
+ "firstAired",
18
+ "network",
19
+ "genre",
20
+ "overview",
21
+ "rating",
22
+ "addedBy",
23
+ "networkId",
24
+ "airsTime"
25
+ ]
26
+ }
27
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "data": {
3
+ "fanart": 11,
4
+ "poster": 9,
5
+ "season": 59,
6
+ "seasonwide": 2,
7
+ "series": 7
8
+ }
9
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "data": [
3
+ {
4
+ "id": 24665,
5
+ "keyType": "fanart",
6
+ "subKey": "",
7
+ "fileName": "fanart/original/76703-1.jpg",
8
+ "resolution": "1280x720",
9
+ "ratingsInfo": {
10
+ "average": 6
11
+ }
12
+ },
13
+ {
14
+ "id": 42154,
15
+ "keyType": "fanart",
16
+ "subKey": "",
17
+ "fileName": "fanart/original/76703-2.jpg",
18
+ "resolution": "1280x720",
19
+ "ratingsInfo": {
20
+ "average": 6.2
21
+ }
22
+ },
23
+ {
24
+ "id": 42155,
25
+ "keyType": "fanart",
26
+ "subKey": "",
27
+ "fileName": "fanart/original/76703-3.jpg",
28
+ "resolution": "1280x720",
29
+ "ratingsInfo": {
30
+ "average": 6.375
31
+ }
32
+ }
33
+ ],
34
+ "errors": null
35
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "data": [
3
+ {
4
+ "keyType": "fanart",
5
+ "resolution": [
6
+ "1920x1080",
7
+ "1280x720"
8
+ ],
9
+ "subKey": [
10
+ "graphical",
11
+ "text"
12
+ ]
13
+ },
14
+ {
15
+ "keyType": "poster",
16
+ "resolution": "680x1000",
17
+ "subKey": null
18
+ },
19
+ {
20
+ "keyType": "season",
21
+ "resolution": null,
22
+ "subKey": [
23
+ "13",
24
+ "7",
25
+ "6",
26
+ "5",
27
+ "4",
28
+ "3",
29
+ "2",
30
+ "1",
31
+ "14",
32
+ "15",
33
+ "8",
34
+ "9",
35
+ "10",
36
+ "16",
37
+ "12",
38
+ "11",
39
+ "0"
40
+ ]
41
+ },
42
+ {
43
+ "keyType": "seasonwide",
44
+ "resolution": null,
45
+ "subKey": [
46
+ "10",
47
+ "1"
48
+ ]
49
+ },
50
+ {
51
+ "keyType": "series",
52
+ "resolution": null,
53
+ "subKey": [
54
+ "graphical",
55
+ "blank",
56
+ "text"
57
+ ]
58
+ }
59
+ ]
60
+ }
@@ -0,0 +1,6 @@
1
+ ---
2
+ tvdb:
3
+ host_url: https://api.thetvdb.com
4
+ username: foo
5
+ password: bar
6
+ api_key: 123abc
@@ -0,0 +1,13 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+
4
+ shared_context "Authentication" do
5
+ include_context "Credentials"
6
+ include_context "Connection"
7
+
8
+ def authenticate_connection
9
+ valid_creds[:connection] = connection
10
+
11
+ TVDB::Authorization.new( valid_creds ).login
12
+ end
13
+ end
@@ -0,0 +1,20 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+
4
+ shared_context "Connection" do
5
+ let( :connection_options ) {
6
+ {
7
+ host_url: "https://api.thetvdb.localhost.com"
8
+ }
9
+ }
10
+
11
+ let( :connection ) {
12
+ TVDB::Connection.new( host_url: connection_options[:host_url] )
13
+ }
14
+
15
+ def authenticate_connection
16
+ valid_creds[:connection] = connection
17
+
18
+ TVDB::Authorization.new( valid_creds ).login
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+
4
+ shared_context "Credentials" do
5
+ let( :valid_creds ) {
6
+ {
7
+ :username => "validuser",
8
+ :userpass => "validpass",
9
+ :apikey => "validapikey"
10
+ }
11
+ }
12
+
13
+ let( :invalid_creds ) {
14
+ {
15
+ :username => "invaliduser",
16
+ :userpass => "invalidpass",
17
+ :apikey => "invalidapikey"
18
+ }
19
+ }
20
+ end
@@ -0,0 +1,19 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+
4
+ shared_context "Series Subclass" do
5
+ include_context "Authentication"
6
+
7
+ before( :each ) do
8
+ authenticate_connection
9
+ end
10
+
11
+ let( :series_id ) { '76703' }
12
+
13
+ let( :series_opts ) {
14
+ {
15
+ connection: connection,
16
+ series_id: series_id,
17
+ }
18
+ }
19
+ end
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+ require 'json'
3
+
4
+ describe "TVDB::Authorization" do
5
+
6
+ include_context "Connection"
7
+ include_context "Credentials"
8
+
9
+ let( :valid_init_options ) {
10
+ valid_creds[:connection] = connection
11
+ valid_creds
12
+ }
13
+
14
+ let( :invalid_init_options ) {
15
+ invalid_creds[:connection] = connection
16
+ invalid_creds
17
+ }
18
+
19
+ subject { TVDB::Authorization.new( valid_init_options ) }
20
+
21
+ describe "initialization" do
22
+ it "should create a TVDB::Authorization object" do
23
+ expect( subject ).to be_a_kind_of( TVDB::Authorization )
24
+ end
25
+
26
+ it "should have a connection accessor" do
27
+ expect( subject ).to respond_to( :connection )
28
+
29
+ subject.connection.token = "pudding"
30
+ expect( subject.connection.token ).to eq( "pudding" )
31
+ end
32
+ end
33
+
34
+ describe "Authentication" do
35
+ context 'Logging in' do
36
+ it "should be able to log in" do
37
+ expect( subject ).to respond_to( :login )
38
+ end
39
+
40
+ it "should set a token on the connection object upon successful login" do
41
+ subject.login
42
+
43
+ expect( subject.connection.token ).to be_a_kind_of( String )
44
+ expect( subject.connection.token.length > 1 ).to be( true )
45
+ end
46
+
47
+ it "should not set a token upon failed login" do
48
+ failure = TVDB::Authorization.new( invalid_init_options )
49
+ failure.login
50
+
51
+ expect( failure.connection.token.length > 1 ).to be( false )
52
+ expect( failure.login.code ).to be( 401 )
53
+ end
54
+ end
55
+
56
+ context 'Refreshing tokens' do
57
+ it "should refresh a valid token" do
58
+ subject.login
59
+ old_token = subject.connection.token
60
+
61
+ subject.refresh_token
62
+ new_token = subject.connection.token
63
+
64
+ expect( old_token ).not_to eq( new_token )
65
+ end
66
+ end
67
+ end
68
+
69
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+ require 'json'
3
+
4
+ describe "TVDB::Client" do
5
+
6
+ include_context "Credentials"
7
+
8
+ subject { TVDB::Client.new( valid_creds ) }
9
+
10
+ describe "Initialization" do
11
+ it "should require user credentials" do
12
+ expect { TVDB::Client.new( invalid_creds ) }.to raise_error( RuntimeError )
13
+ end
14
+
15
+ it "should authenticate the user and set a token" do
16
+ expect( subject.connection.token ).to be_a_kind_of( String )
17
+ end
18
+
19
+ it "should set an auth accessor" do
20
+ expect( subject.auth ).to be_a_kind_of( TVDB::Authorization )
21
+ end
22
+ end
23
+
24
+ describe "Authentication" do
25
+ it "should refresh the user's token" do
26
+ old_token = subject.connection.token
27
+ subject.refresh_token
28
+
29
+ expect( subject.connection.token ).not_to eq( old_token )
30
+ end
31
+ end
32
+
33
+ describe "Functionality" do
34
+ context 'Series' do
35
+ it "should return a series object" do
36
+ pokemon = subject.series( '76703')
37
+
38
+ expect( pokemon ).to be_a_kind_of( TVDB::Series )
39
+ expect( pokemon.data ).to be_a_kind_of( Hash )
40
+ expect( pokemon.data ).to have_key( "data" )
41
+ end
42
+ end
43
+ end
44
+
45
+ end
@@ -0,0 +1,159 @@
1
+ require 'spec_helper'
2
+ require 'json'
3
+
4
+ describe "TVDB::Connection" do
5
+
6
+ include_context "Connection"
7
+
8
+ let( :convenience_headers ) {
9
+ {:language => "derp", :version => "12345", :modified_since => "Tuesday"}
10
+ }
11
+
12
+ subject { TVDB::Connection.new( connection_options ) }
13
+
14
+ describe "Initialization" do
15
+
16
+ it "should createa a connection and set attrs" do
17
+ expect( subject.connection ).to be_a_kind_of( Faraday::Connection )
18
+ expect( subject.host_url ).to eq( connection_options[:host_url] )
19
+ expect( subject.token ).to eq( "" )
20
+ end
21
+
22
+ it "should setup a Response struct" do
23
+ expect( subject.response_struct ).to be_a_kind_of( Class )
24
+ end
25
+
26
+ end
27
+
28
+ describe "Ancillary operations" do
29
+
30
+ context 'headers' do
31
+ it "should set default headers for requests" do
32
+ expect( subject.set_default_headers( {} ) ).to be_a_kind_of( Hash )
33
+ expect( subject.set_default_headers( {} )["Content-Type"] ).to eq( "application/json" )
34
+ expect( subject.set_default_headers( {} )["Authorization"] ).to eq( "Bearer " )
35
+ end
36
+
37
+ it "should override default headers " do
38
+ custom_header = { :headers => { "Content-Type" => "text/html" } }
39
+
40
+ expect( subject.set_default_headers( custom_header )["Content-Type"] ).to eq( "text/html" )
41
+ end
42
+
43
+ it "should apply custom headers" do
44
+ custom_header = { :headers => { "howdy" => "g'day mate" } }
45
+
46
+ expect( subject.set_default_headers( custom_header )["howdy"] ).to eq( "g'day mate" )
47
+ end
48
+
49
+ it "should set the langauge or version if passed in" do
50
+ subject.set_convenience_headers( convenience_headers )
51
+
52
+ expect( subject.language ).to eq( "derp" )
53
+ expect( subject.version ).to eq( "12345" )
54
+ expect( subject.modified_since ).to eq( "Tuesday" )
55
+ end
56
+ end
57
+
58
+ context 'responses' do
59
+ let( :route ) { "/login" }
60
+ let( :response ) { subject.post( route, body: {} ) }
61
+
62
+ it "should store responses in a struct" do
63
+ expect( response ).to be_a_kind_of( Struct )
64
+ end
65
+
66
+ it "should set a code accessor" do
67
+ expect( response ).to respond_to( :code )
68
+ expect( response.code ).to eq( 401 )
69
+ end
70
+
71
+ it "should set a body accessor" do
72
+ expect( response ).to respond_to( :body )
73
+ expect( response.body ).to eq( {"Error"=>"API Key Required"} )
74
+ end
75
+
76
+ it "should set a headers accessor" do
77
+ expect( response ).to respond_to( :headers )
78
+ expect( response.headers ).to be_a_kind_of( Hash )
79
+ end
80
+
81
+ it "should set a request_url accessor" do
82
+ expect( response ).to respond_to( :request_url )
83
+ expect( response.request_url ).to eq( "#{connection_options[:host_url]}#{route}")
84
+ end
85
+ end
86
+ end
87
+
88
+ describe "REST operations" do
89
+ include_context "Credentials"
90
+
91
+ describe 'POSTing' do
92
+
93
+ let( :post_params ) {
94
+ {
95
+ :body => { :bacon => "tasty" }
96
+ }
97
+ }
98
+
99
+ it "should be able to POST json to a route" do
100
+ expect( subject ).to respond_to( :post )
101
+ end
102
+
103
+ context 'Authorized requests' do
104
+ let( :successful_post ) {
105
+ subject.post( '/login', body: valid_creds )
106
+ }
107
+
108
+ it "should return a 200 upon successful POST" do
109
+ expect( successful_post.code ).to be( 200 )
110
+ end
111
+
112
+ it "should return response headers" do
113
+ expect( successful_post.headers["content-type"] ).to eq( "application/json" )
114
+ end
115
+ end
116
+
117
+ context 'Unauthorized requests' do
118
+ it "should return a 401 if unathorized" do
119
+ expect( subject.post( '/login', post_params ).code ).to be( 401 )
120
+ end
121
+ end
122
+
123
+ end
124
+
125
+ describe "GETing" do
126
+ it "should be able to GET a route" do
127
+ expect( subject ).to respond_to( :get )
128
+ end
129
+
130
+ it "should take optional parameters" do
131
+ param_req = subject.get( '/series/1234', :page => 1 )
132
+ expect( param_req.request_url ).to match( 'page=1' )
133
+ end
134
+
135
+ it "should override existing convenience headers" do
136
+ subject.get( '/series/1234', convenience_headers )
137
+
138
+ convenience_headers.each do |accessor, value|
139
+ expect( subject.send( accessor ) ).to eq( value )
140
+ end
141
+ end
142
+
143
+ context 'Unauthorized requests' do
144
+ it "should return a 401 if unathorized" do
145
+ expect( subject.get( '/series/1234' ).code ).to be( 401 )
146
+ end
147
+ end
148
+
149
+ context '304 response' do
150
+ it "should an empty body for a 304 (not modified) response" do
151
+ expect( subject.get( '/not/modified' ).code ).to be( 304 )
152
+ expect( subject.get( '/not/modified' ).body ).to be( nil )
153
+ end
154
+ end
155
+ end
156
+
157
+ end
158
+
159
+ end