strutta-api 1.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2d72692a864ae1a19c3bf8761741a79dd505d751
4
+ data.tar.gz: b0989b6d0d993f3396d2d7f2d59791dd267d5b63
5
+ SHA512:
6
+ metadata.gz: bac95bc6eaa7f7a19a7d2824ccd2a4a1602b9369b2b5a65fadc64700a6a335b982ce3be9a4cc5ea58936c32588ccb033d386d5579ed8eb9784b04f85d204dec1
7
+ data.tar.gz: a863c2ec69241f28e429bd3a0279ca3e0aeb03ffd18ada8c7a7b4992af8bf1b43f1b97478098bad2243e27525612d0dfcb04c7821b33711df90d50258d6888e9
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rubocop.yml
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in strutta-api.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 BlakeTurner
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,55 @@
1
+ [![Code Climate](https://codeclimate.com/repos/53d81d1de30ba01f16012d2a/badges/5a6842ba6fe26d49d83b/gpa.png)](https://codeclimate.com/repos/53d81d1de30ba01f16012d2a/feed)
2
+ [![Test Coverage](https://codeclimate.com/repos/53d81d1de30ba01f16012d2a/badges/5a6842ba6fe26d49d83b/coverage.png)](https://codeclimate.com/repos/53d81d1de30ba01f16012d2a/feed)
3
+
4
+ # Strutta::Api
5
+
6
+ Official wrapper for the Strutta API
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'strutta-api'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install strutta-api
21
+
22
+ ## Usage
23
+
24
+ This Gem is designed to have is calls resemble actual API URIs.
25
+
26
+ For example, if we want to get Rounds in Game 333, we would GET the following URI:
27
+
28
+ ```
29
+ http://api.strutta.com/v2/games/333/rounds
30
+ ```
31
+
32
+ The Gem copies this pattern:
33
+
34
+ ```
35
+ # Initialize
36
+ strutta = Strutta::API.new 'mystruttatoken'
37
+
38
+ # Get Rounds in Game 333
39
+ strutta.games(333).rounds.get
40
+ ```
41
+
42
+ There are detailed examples for each endpoint found in the Strutta API docs.
43
+
44
+ ## Errors
45
+
46
+ This Gem Raises exceptions whenever anything other than a `200`, `201` or `204` is returned.
47
+ The errors are defined in lib/strutta/api/errors and generated Strutta::Api#error_map.
48
+
49
+ ## Contributing
50
+
51
+ 1. Fork it ( http://github.com/<my-github-username>/strutta-api/fork )
52
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
53
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
54
+ 4. Push to the branch (`git push origin my-new-feature`)
55
+ 5. Create new Pull Request
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task default: :spec
7
+ task test: :spec
@@ -0,0 +1,109 @@
1
+ require 'excon'
2
+ require 'json'
3
+
4
+ require 'strutta-api/api_object.rb'
5
+ require 'strutta-api/entries.rb'
6
+ require 'strutta-api/errors.rb'
7
+ require 'strutta-api/flow.rb'
8
+ require 'strutta-api/games.rb'
9
+ require 'strutta-api/participants.rb'
10
+ require 'strutta-api/points.rb'
11
+ require 'strutta-api/moderation.rb'
12
+ require 'strutta-api/judging.rb'
13
+ require 'strutta-api/rounds.rb'
14
+ require 'strutta-api/version.rb'
15
+
16
+ module Strutta
17
+ # Strutta API engine
18
+ # Handles API requests and errors
19
+ class API
20
+ include Strutta::Version
21
+ attr_accessor :host, :path, :token, :debug, :session
22
+
23
+ # Initializes the Strutta API wrapper
24
+ #
25
+ # @param token [String] your Strutta API token
26
+ # @param host [String] Strutta API host URL - used to switched between prod/staging
27
+ # @param path [String] Strutta API host path - used to switched between versions
28
+ # @return [Strutta::API] instantiated Strutta API object
29
+ def initialize(token, host = 'http://strutta-api.herokuapp.com/', path = Version::VERSION_PATH)
30
+ fail Error, 'You must provide a Strutta API key' unless token
31
+
32
+ @host = host
33
+ @path = path
34
+ @token = token
35
+ @session = Excon.new @host
36
+ @debug = debug
37
+ end
38
+
39
+ # Makes an API call
40
+ #
41
+ # @param method [String] the required HTTP method
42
+ # @param url [String] URL for the call
43
+ # @param params [Hash] Parameters for the call
44
+ # @return [Hash] Parsed body of the response
45
+ def call(method, url, params = {})
46
+ params = JSON.generate(params)
47
+ r = @session.send(method, path: "#{@path}#{url}", headers: api_headers, body: params)
48
+
49
+ # Delete calls have no JSON return
50
+ return true if r.status == 204
51
+
52
+ # Raise exceptions on error response codes
53
+ cast_error(r.status, r.body) if r.status >= 400
54
+
55
+ JSON.parse(r.body)
56
+ end
57
+
58
+ # Instantiates a Strutta::Games object
59
+ #
60
+ # @param id [Integer, nil] the ID of the Strutta game
61
+ # @return [Strutta::Games] The instantiated Strutta::Games object
62
+ def games(id = nil)
63
+ Games.new id, self
64
+ end
65
+
66
+ private
67
+
68
+ # Utility: build the Headers hash
69
+ # @return [Hash] Headers for an API call
70
+ def api_headers
71
+ {
72
+ 'Content-Type' => 'application/json',
73
+ 'Authorization' => "Token token=#{@token}"
74
+ }
75
+ end
76
+
77
+ # Throws exceptions based on HTTP responses
78
+ #
79
+ # @param status [Integer] the HTTP response status
80
+ # @param body [JSON] the HTTP response body
81
+ # @return [Error] An appropriate exception
82
+ def cast_error(status, body)
83
+ error_info = JSON.parse(body)
84
+ msg = error_info['error'] && error_info['message'] ? "#{error_info['error']} - #{error_info['message']}" : "We received an unexpected error: #{body}"
85
+ fail error_map(status), msg
86
+ rescue JSON::ParserError
87
+ raise Errors::Error, "We received an unexpected error: #{body}"
88
+ end
89
+
90
+ # Map errors to HTTP statuses
91
+ #
92
+ # @param status [Integer] the HTTP response status
93
+ # @return [Error] The associated exception
94
+ def error_map(status)
95
+ case status
96
+ when 401
97
+ Errors::Unauthorized
98
+ when 400
99
+ Errors::BadRequestError
100
+ when 404
101
+ Errors::ObjectNotFoundError
102
+ when 422
103
+ Errors::UnprocessableEntityError
104
+ else
105
+ Errors::Error
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,58 @@
1
+ # Strutta Container
2
+ module Strutta
3
+ # @abstract
4
+ # APIObject contains all the HTTP request methods
5
+ class APIObject
6
+ attr_accessor :game
7
+
8
+ # GET index request
9
+ # (Uses instance vars of child object to generate resource path)
10
+ #
11
+ # @return [Hash] Parsed body of the API response
12
+ def all(params = {})
13
+ @game.verify_no_id(@id)
14
+ @game.all(params, "#{@game.id}/#{@root_path}")
15
+ end
16
+
17
+ # POST request
18
+ # (Uses instance vars of child object to generate resource path)
19
+ #
20
+ # @return [Hash] Parsed body of the API response
21
+ def create(params = {})
22
+ @game.verify_no_id(@id)
23
+ @game.create(params, "#{@game.id}/#{@root_path}")
24
+ end
25
+
26
+ # GET request
27
+ # (Uses instance vars of child object to generate resource path)
28
+ #
29
+ # @return [Hash] Parsed body of the API response
30
+ def get(params = {})
31
+ @game.verify_id(@id, Errors::ROUND_ID_REQUIRED)
32
+ @game.get(params, @root_path)
33
+ end
34
+
35
+ # PATCH request
36
+ # (Uses instance vars of child object to generate resource path)
37
+ #
38
+ # @return [Hash] Parsed body of the API response
39
+ def update(params = {})
40
+ @game.verify_id(@id, Errors::ROUND_ID_REQUIRED)
41
+ @game.update(params, @root_path)
42
+ end
43
+
44
+ # DELETE request
45
+ # (Uses instance vars of child object to generate resource path)
46
+ #
47
+ # @return [Hash] Parsed body of the API response
48
+ def delete
49
+ @game.verify_id(@id, Errors::ROUND_ID_REQUIRED)
50
+ @game.delete(@root_path)
51
+ end
52
+
53
+ # Manually disable a method in child classed
54
+ def method_disabled(*_)
55
+ fail Errors::DisabledEndpointError, Errors::METHOD_DISABLED
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,35 @@
1
+ module Strutta
2
+ # Entries belong to a Strutta::Games object
3
+ # Instance methods found in Strutta::APIObject
4
+ class Entries < APIObject
5
+ # Initializes the Strutta::Entries object
6
+ #
7
+ # @param id [Integer, nil] Entry id
8
+ # @param game [Strutta::Games] Master Strutta::Games object
9
+ # @return [Strutta::Entries] instantiated Strutta::Entries object
10
+ def initialize(id = nil, game)
11
+ @id = id
12
+ @game = game
13
+ @root_path = "entries/#{@id}"
14
+ @no_id_error = Errors::ENTRY_ID_REQUIRED
15
+ end
16
+
17
+ # GET transition history for Entry (no ID required)
18
+ # games/:game_id/entries/:id/transitions
19
+ #
20
+ # @return [Hash] Parsed body of the API response
21
+ def transitions(params = {})
22
+ @game.verify_id(@id, Errors::ENTRY_ID_REQUIRED)
23
+ @game.get(params, "entries/#{@id}/transitions")
24
+ end
25
+
26
+ # GET leaderboard history for Entries in Points Rounds (no ID required)
27
+ # games/:game_id/entries/leaderboard
28
+ #
29
+ # @return [Hash] Parsed body of the API response
30
+ def leaderboard(params = {})
31
+ @game.verify_no_id(@id)
32
+ @game.get(params, 'entries/leaderboard')
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,47 @@
1
+ module Strutta
2
+ # Strutta Wrapper Errors
3
+ module Errors
4
+ # Game ID is required for .get, .update, .delete
5
+ GAME_ID_REQUIRED = 'Game ID required'
6
+ # Round ID is required for .get, .update, .delete
7
+ ROUND_ID_REQUIRED = 'Round ID required'
8
+ # Participant ID is required for .get, .update, .delete
9
+ PARTICIPANT_ID_REQUIRED = 'Participant ID required'
10
+ # Entry ID is required for .get, .update, .delete
11
+ ENTRY_ID_REQUIRED = 'Entry ID required'
12
+ # ID is not allowed for some methods
13
+ OBJECT_NOT_ALLOWED = 'This method may not have an ID associated with it. Example: strutta.games.entries.all instead of strutta.games.entries(ENTRY_ID).all'
14
+ # ID is not allowed for some methods
15
+ INVALID_SEARCH = 'This version of the API only support searching by email'
16
+ # Some http verbs are disabled for object, tell the user nicely
17
+ METHOD_DISABLED = 'This method is disabled for this object type'
18
+
19
+ # Generic Errors
20
+ class Error < StandardError
21
+ end
22
+ # 400 Errors
23
+ class BadRequestError < Error
24
+ end
25
+ # 401 Errors
26
+ class Unauthorized < Error
27
+ end
28
+ # 404 Errors
29
+ class ObjectNotFoundError < Error
30
+ end
31
+ # 422 Errors
32
+ class UnprocessableEntityError < Error
33
+ end
34
+ # .get, .update, .delete requests require an object ID
35
+ class ObjectIDRequired < Error
36
+ end
37
+ # .all, .create do not allow an abject ID
38
+ class ObjectIDNotAllowed < Error
39
+ end
40
+ # Enforce searching only by email
41
+ class InvalidSearchParameters < Error
42
+ end
43
+ # Enforce disabled endpoints
44
+ class DisabledEndpointError < Error
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,35 @@
1
+ module Strutta
2
+ # Flow belongs to a Strutta::Games object
3
+ # Other Instance methods found in Strutta::APIObject
4
+ class Flow < APIObject
5
+ # Initializes the Strutta::Flow object
6
+ #
7
+ # @param game [Strutta::Games] Master Strutta::Games object
8
+ # @return [Strutta::Flow] instantiated Strutta::Flow object
9
+ def initialize(id = nil, game)
10
+ @id = id
11
+ @game = game
12
+ @root_path = 'flow'
13
+ end
14
+
15
+ # GET request for Flow (no ID required)
16
+ #
17
+ # @return [Hash] Parsed body of the API response
18
+ def get
19
+ @game.verify_no_id(@id)
20
+ @game.get({}, @root_path)
21
+ end
22
+
23
+ # DELETE request for Flow (no ID required)
24
+ #
25
+ # @return [Hash] Parsed body of the API response
26
+ def delete
27
+ @game.verify_no_id(@id)
28
+ @game.delete(@root_path)
29
+ end
30
+
31
+ # Disbled methods
32
+ alias_method :update, :method_disabled
33
+ alias_method :all, :method_disabled
34
+ end
35
+ end
@@ -0,0 +1,159 @@
1
+ module Strutta
2
+ # The Strutta Games object is the master of all other API objects (Rounds, Flow, Entries, Participants)
3
+ # The above objects are instantiated in association with a Games object, as each belong to a Game in the Strutta API
4
+ # The above objects also have all their API requests forwarded through this object to Strutta::API
5
+ class Games
6
+ attr_accessor :master, :id
7
+
8
+ # Initializes the Strutta::Games object
9
+ #
10
+ # @param id [Integer, nil] Game id
11
+ # @param master [Strutta::API] Master Strutta::API object
12
+ # @return [Strutta::Games] instantiated Strutta::Games object
13
+ def initialize(id = nil, master)
14
+ @id = id
15
+ @root_path = 'games'
16
+ @master = master
17
+ end
18
+
19
+ # Forwards an API call to @master
20
+ #
21
+ # @param method [String] the required HTTP method
22
+ # @param url [String] URL for the call
23
+ # @param params [Hash] Parameters for the call
24
+ # @return [Hash] Parsed body of the response
25
+ def call(method, url, params = {})
26
+ @master.call(method, url, params)
27
+ end
28
+
29
+ # Polymorphic GET Index request
30
+ #
31
+ # @param resource_path [String, nil] the GET index path for the requesting object
32
+ # See Strutta::APIObject#all
33
+ # @return [Hash] Parsed body of the response
34
+ def all(params = {}, resource_path = nil)
35
+ verify_no_id(@id) unless resource_path
36
+ call('get', "#{@root_path}/#{resource_path}", params)
37
+ end
38
+
39
+ # Polymorphic GET single object request
40
+ #
41
+ # @param resource_path [String, nil] the GET path for the requesting object
42
+ # See Strutta::APIObject#get
43
+ # @return [Hash] Parsed body of the response
44
+ def get(params = {}, resource_path = '')
45
+ verify_id(@id, Errors::GAME_ID_REQUIRED)
46
+ call('get', "#{@root_path}/#{@id}/#{resource_path}", params)
47
+ end
48
+
49
+ # Polymorphic POST request
50
+ #
51
+ # @param resource_path [String, nil] the POST path for the requesting object
52
+ # See Strutta::APIObject#create
53
+ # @return [Hash] Parsed body of the response
54
+ def create(params = {}, resource_path = nil)
55
+ verify_no_id(@id) unless resource_path
56
+ call('post', "#{@root_path}/#{resource_path}", params)
57
+ end
58
+
59
+ # Polymorphic PATCH request
60
+ #
61
+ # @param resource_path [String, nil] the PATCH path for the requesting object
62
+ # See Strutta::APIObject#update
63
+ # @return [Hash] Parsed body of the response
64
+ def update(params, resource_path = nil)
65
+ verify_id(@id, Errors::GAME_ID_REQUIRED)
66
+ call('patch', "#{@root_path}/#{@id}/#{resource_path}", params)
67
+ end
68
+
69
+ # Polymorphic DELETE request
70
+ #
71
+ # @param resource_path [String, nil] the DELETE path for the requesting object
72
+ # See Strutta::APIObject#delete
73
+ # @return [Hash] Parsed body of the response
74
+ def delete(resource_path = nil)
75
+ verify_id(@id, Errors::GAME_ID_REQUIRED)
76
+ call('delete', "#{@root_path}/#{@id}/#{resource_path}", {})
77
+ end
78
+
79
+ # Instantiates a Strutta::Rounds object with reference to this Game
80
+ #
81
+ # @param id [Integer, nil] the ID of the Round
82
+ # @return [Strutta::Rounds] The instantiated Strutta::Rounds object
83
+ def rounds(id = nil)
84
+ verify_id(@id, Errors::GAME_ID_REQUIRED)
85
+ Rounds.new id, self
86
+ end
87
+
88
+ # Instantiates a Strutta::Participants object with reference to this Game
89
+ #
90
+ # @param id [Integer, nil] the ID of the Participant
91
+ # @return [Strutta::Participants] The instantiated Strutta::Participants object
92
+ def participants(id = nil)
93
+ verify_id(@id, Errors::GAME_ID_REQUIRED)
94
+ Participants.new id, self
95
+ end
96
+
97
+ # Instantiates a Strutta::Flow object with reference to this Game
98
+ #
99
+ # @param id [Integer, nil] Flows do not have Ids. This is here simply for consistency, and will result in error of used.
100
+ # @return [Strutta::Flow] The instantiated Strutta::Flow object
101
+ def flow(id = nil)
102
+ verify_id(@id, Errors::GAME_ID_REQUIRED)
103
+ Flow.new id, self
104
+ end
105
+
106
+ # Instantiates a Strutta::Entries object with reference to this Game
107
+ #
108
+ # @param id [Integer, nil] the ID of the Entry
109
+ # @return [Strutta::Entries] The instantiated Strutta::Entries object
110
+ def entries(id = nil)
111
+ verify_id(@id, Errors::GAME_ID_REQUIRED)
112
+ Entries.new id, self
113
+ end
114
+
115
+ # Instantiates a Strutta::Points object with reference to this Game
116
+ #
117
+ # @param id [Integer, nil] Point IDs are not used by this API. This is here simply for consistency, and will result in error of used.
118
+ # @return [Strutta::Entries] The instantiated Strutta::Points object
119
+ def points(id = nil)
120
+ verify_id(@id, Errors::GAME_ID_REQUIRED)
121
+ Points.new id, self
122
+ end
123
+
124
+ # Instantiates a Strutta::Moderation object with reference to this Game
125
+ #
126
+ # @param id [Integer, nil] Moderation do not have Ids. This is here simply for consistency, and will result in error of used.
127
+ # @return [Strutta::Entries] The instantiated Strutta::Moderation object
128
+ def moderation(id = nil)
129
+ verify_id(@id, Errors::GAME_ID_REQUIRED)
130
+ Moderation.new id, self
131
+ end
132
+
133
+ # Instantiates a Strutta::Judging object with reference to this Game
134
+ #
135
+ # @param id [Integer, nil] Judging does not have Ids. This is here simply for consistency, and will result in error of used.
136
+ # @return [Strutta::Entries] The instantiated Strutta::Judging object
137
+ def judging(id = nil)
138
+ verify_id(@id, Errors::GAME_ID_REQUIRED)
139
+ Judging.new id, self
140
+ end
141
+
142
+ # Confirm that an id exists for a request
143
+ #
144
+ # @param id [Integer] the ID of the objec
145
+ # @param message [String] Error message if no id exists
146
+ # @return [nil] nil if id exists, Error otherwise
147
+ def verify_id(id, message)
148
+ fail Errors::ObjectIDRequired, message unless id
149
+ end
150
+
151
+ # Confirm that no id exists for a request
152
+ #
153
+ # @param id [Integer] the ID of the objec
154
+ # @return [nil] nil if no id exists, Error otherwise
155
+ def verify_no_id(id)
156
+ fail Errors::ObjectIDNotAllowed, Errors::OBJECT_NOT_ALLOWED if id
157
+ end
158
+ end
159
+ end