projector 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/Overview.md +66 -0
- data/README.md +1 -1
- data/Rakefile +1 -1
- data/lib/projector.rb +12 -20
- data/lib/projector/api_methods.rb +56 -2
- data/lib/projector/client.rb +28 -6
- data/lib/projector/end_user.rb +6 -3
- data/lib/projector/envelope.rb +8 -5
- data/lib/projector/error.rb +25 -29
- data/lib/projector/event.rb +9 -2
- data/lib/projector/transport/http.rb +51 -6
- data/lib/projector/version.rb +4 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e5207d8f41bed6a38021ab35f4593d04b30e3acc
|
4
|
+
data.tar.gz: b1c1928c82e355a58e2bb3c1b30b93dd4f0a45d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e498f9b613d6562a6fe07d39ce8d5070c58c3a762f687fddc431af3e87e709b1cd9c792977b1fda040b54b61dd0fda72fe020e353085202de78225f95c8c776f
|
7
|
+
data.tar.gz: 8568bd726be3187241a39508af57f23707a21d792ff758d1a7542295581c519825666ea90ec7ef6138090ee5dfee2f4060785fb0789035392140256f4f5b197a
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
yardoc --protected ./lib/**/*.rb --readme Overview.md
|
data/Overview.md
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# Projector Ruby SDK
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
Add the Projector Ruby SDK gem to your Gemfile with the following:
|
5
|
+
|
6
|
+
```
|
7
|
+
gem 'projector'
|
8
|
+
```
|
9
|
+
|
10
|
+
Install it by running the following using bundler:
|
11
|
+
|
12
|
+
```
|
13
|
+
bundle install
|
14
|
+
```
|
15
|
+
|
16
|
+
## Configuration
|
17
|
+
See {Projector::Client} for information on configuring the client.
|
18
|
+
|
19
|
+
## Interacting with the Projector API
|
20
|
+
See {Projector::ApiMethods} for information on public API methods exposed in this SDK.
|
21
|
+
|
22
|
+
## Handling Errors
|
23
|
+
If an error occurs, an exception will be thrown which the user can catch and inspect for further details.
|
24
|
+
|
25
|
+
The Projector SDK can encounter two types of errors when communicating with Projector's systems - Internal Server and Client Errors. All errors subclass from {Projector::Error}.
|
26
|
+
|
27
|
+
### Client Errors
|
28
|
+
The Projector SDK has two types of Client Errors - {Projector::Unauthorized} and {Projector::BadRequest}. Making a request with safeguards for both can be done as follows:
|
29
|
+
|
30
|
+
```
|
31
|
+
begin
|
32
|
+
Projector.register_end_user(end_user)
|
33
|
+
rescue Projector::Unauthorized => auth_error
|
34
|
+
puts auth_error.response_body
|
35
|
+
rescue Projector::BadRequest => bad_request_error
|
36
|
+
puts bad_request_error.response_body
|
37
|
+
else
|
38
|
+
puts 'Unknown error'
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
### Internal Server Errors
|
43
|
+
These type of errors can happen for a number of reasons; our servers might be at capacity and we are in the process of scaling them up, a request might have hit an instance being removed from service or other intermittent failure. Making a request can be safeguarded from then as follows:
|
44
|
+
|
45
|
+
```
|
46
|
+
begin
|
47
|
+
Projector.register_end_user(end_user)
|
48
|
+
rescue Projector::InternalServerError => server_error
|
49
|
+
if server_error.retry_after
|
50
|
+
sleep server_error.retry_after
|
51
|
+
else
|
52
|
+
#Retry later
|
53
|
+
end
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
You can retry repeatedly but if the error does not eventually clear up reach out to Projector and we will look into the issue with you. Please include the Request ID, also available as a property on the exception.
|
58
|
+
|
59
|
+
```
|
60
|
+
begin
|
61
|
+
Projector.register_end_user(end_user)
|
62
|
+
rescue Projector::InternalServerError => server_error
|
63
|
+
# If the error does not recover, notify Projector including the Request Id
|
64
|
+
puts server_error.request_id
|
65
|
+
end
|
66
|
+
```
|
data/README.md
CHANGED
data/Rakefile
CHANGED
@@ -21,7 +21,7 @@ task :default => :test
|
|
21
21
|
|
22
22
|
desc "Publish SDK docs to GitHub Pages"
|
23
23
|
task :publish_docs do
|
24
|
-
system("set -x; bundle exec yardoc
|
24
|
+
system("set -x; bundle exec yardoc") or abort
|
25
25
|
system("set -x; mv -v ./doc /tmp ") or abort
|
26
26
|
system("set -x; git checkout gh-pages ") or abort
|
27
27
|
system("set -x; cp -r /tmp/doc/* . ") or abort
|
data/lib/projector.rb
CHANGED
@@ -1,26 +1,35 @@
|
|
1
1
|
require 'projector/version'
|
2
2
|
require 'projector/client'
|
3
3
|
|
4
|
+
# Main interface for interacting with the Projector SDK. This class forwards method calls to an
|
5
|
+
# internal instance of {Projector::Client}, allowing you to make calls like {Projector::ApiMethods#deliver_event Projector.deliver_event(event)}
|
6
|
+
# without explicitly constructing an instance. Instances constructed in this way will use the default
|
7
|
+
# options specified using either {Projector::Client 12-factor environment variables} or via {Projector::Client.configure}.
|
8
|
+
#
|
9
|
+
# If you wish to keep a persistent {Projector::Client} instance for some reason, construct it using {Projector::Client#initialize new Projector::Client(options)}.
|
4
10
|
module Projector
|
5
11
|
class << self
|
6
12
|
# Alias for Projector::Client.new
|
7
13
|
#
|
14
|
+
# @param [Hash] options configuration options
|
8
15
|
# @return [Projector::Client]
|
9
16
|
def new(options = {})
|
10
17
|
Projector::Client.new(options)
|
11
18
|
end
|
12
19
|
|
13
|
-
# Delegate to Projector::Client
|
20
|
+
# Delegate to an instance of {Projector::Client}
|
14
21
|
def method_missing(method, *args, &block)
|
15
22
|
return super unless new.respond_to?(method)
|
16
23
|
new.send(method, *args, &block)
|
17
24
|
end
|
18
25
|
|
19
|
-
# Forward respond_to? to Projector::Client.
|
26
|
+
# Forward respond_to? to an instance of {Projector::Client}.
|
20
27
|
def respond_to?(method, include_private = false)
|
21
28
|
new.respond_to?(method, include_private) || super(method, include_private)
|
22
29
|
end
|
23
30
|
|
31
|
+
# The Logger instance for internal logging. Defaults to the Rails logger or STDOUT.
|
32
|
+
# @return [Logger]
|
24
33
|
def logger
|
25
34
|
@logger ||= begin
|
26
35
|
if defined?(::Rails)
|
@@ -32,26 +41,9 @@ module Projector
|
|
32
41
|
end
|
33
42
|
end
|
34
43
|
|
44
|
+
# Sets the logger instance for the Projector SDK
|
35
45
|
def logger=(x)
|
36
46
|
@logger = x
|
37
47
|
end
|
38
|
-
|
39
|
-
DEFAULT_ERROR_HANDLER = Proc.new do |ex, ctx|
|
40
|
-
Projector.logger.error(ex)
|
41
|
-
Projector.logger.error(ctx.inspect)
|
42
|
-
ex.backtrace.each do |line|
|
43
|
-
Projector.logger.error(line)
|
44
|
-
end if ex.backtrace
|
45
|
-
end
|
46
|
-
|
47
|
-
##
|
48
|
-
# Add a global error handler for any async errors Projector encounters.
|
49
|
-
# Error handlers must respond to `call(exception, context_hash)`:
|
50
|
-
#
|
51
|
-
# Projector.error_handlers << Proc.new {|ex, ctx| do_something(ex) }
|
52
|
-
#
|
53
|
-
def error_handlers
|
54
|
-
@handlers ||= [DEFAULT_ERROR_HANDLER]
|
55
|
-
end
|
56
48
|
end
|
57
49
|
end
|
@@ -1,5 +1,21 @@
|
|
1
1
|
module Projector
|
2
|
+
# Ruby method wrappers for the Projector API.
|
2
3
|
module ApiMethods
|
4
|
+
|
5
|
+
# Registers or updates an end user at Projector
|
6
|
+
#
|
7
|
+
# @param [Hash] end_user a hash of the end user object
|
8
|
+
# @option end_user [String] :id the ID of the user in your application
|
9
|
+
# @option end_user [Array] :tags an array of tags to apply to the user
|
10
|
+
# @return [Hash] a hash of data that should be returned as part of the registration API call
|
11
|
+
# @example Register an end user
|
12
|
+
# end_user = {
|
13
|
+
# id: '42',
|
14
|
+
# tags: ['likes_dogs']
|
15
|
+
# }
|
16
|
+
# Projector.register_end_user(end_user)
|
17
|
+
# @raise {Projector::Unauthorized}
|
18
|
+
# @raise {Projector::BadRequest}
|
3
19
|
def register_end_user(end_user)
|
4
20
|
# If a hash is passed, use it to populate a user object
|
5
21
|
if end_user.instance_of?(Hash)
|
@@ -9,14 +25,52 @@ module Projector
|
|
9
25
|
user = end_user
|
10
26
|
end
|
11
27
|
# return the data element of the resulting envelope
|
12
|
-
post(host, '/end_users', Projector::Envelope.new(
|
28
|
+
post(host, '/end_users', Projector::Envelope.new(user).to_hash).body
|
13
29
|
end
|
14
30
|
|
31
|
+
# Updates tags on an end user at Projector
|
32
|
+
#
|
33
|
+
# @param [String] id the ID of the user in your application
|
34
|
+
# @param [Array] tags an array of tags to apply to the user
|
35
|
+
# @example Update a user's tags
|
36
|
+
# Projector.set_end_user_tags('51', ['likes_dogs', 'has_dog'])
|
37
|
+
# @raise {Projector::Unauthorized}
|
38
|
+
# @raise {Projector::BadRequest}
|
39
|
+
# @return [Hash]
|
15
40
|
def set_end_user_tags(id, tags)
|
16
41
|
user = Projector::EndUser.new(id: id, tags: tags)
|
17
42
|
register_end_user(user)
|
18
43
|
end
|
19
44
|
|
45
|
+
# Sends an event to one or more users at Projector
|
46
|
+
#
|
47
|
+
# @param [Hash] event a hash of the event object
|
48
|
+
# @option event [String] :id (SecureRandom.uuid) the ID the event
|
49
|
+
# @option event [String] :event_key the event key for this type of event
|
50
|
+
# @option event [String] :event_time (Time.now) the event time in ms from epoch for this event
|
51
|
+
# @option event [Hash] :target the recipient or recipients of the event
|
52
|
+
# @option event [Hash] :event_context data for use in delivery rules and notification alert templates at Projector
|
53
|
+
# @option event [Hash] :payload JSON data properties to be delivered on the event (deep links, metadata, etc)
|
54
|
+
# @example Sending a notification to one or more individual users
|
55
|
+
# event = {
|
56
|
+
# id: 'like-event-7391',
|
57
|
+
# event_key: 'liked-your-dog',
|
58
|
+
# event_context: {dog_name: 'Barclay', liker_name: 'Jana', liker_id: '42'},
|
59
|
+
# target: {end_users: [{id: '51'}]}
|
60
|
+
# }
|
61
|
+
# Projector.deliver_event(event)
|
62
|
+
# @example Sending a notification to one or more tags
|
63
|
+
# event = {
|
64
|
+
# id: 'new-dog-492',
|
65
|
+
# event_key: 'new-dog',
|
66
|
+
# event_context: {dog_name: 'Pixel', color: 'blond', size: 'small'},
|
67
|
+
# target: {tags: ['likes_dogs']}
|
68
|
+
# }
|
69
|
+
# Projector.deliver_event(event)
|
70
|
+
# @raise {Projector::InvalidEvent}
|
71
|
+
# @raise {Projector::Unauthorized}
|
72
|
+
# @raise {Projector::BadRequest}
|
73
|
+
# @return [Hash]
|
20
74
|
def deliver_event(event)
|
21
75
|
# Create an Event instance if the parameter is a Hash.
|
22
76
|
if event.instance_of?(Hash)
|
@@ -24,7 +78,7 @@ module Projector
|
|
24
78
|
end
|
25
79
|
# Verify that we've got an Event object.
|
26
80
|
raise Projector::InvalidEvent.new("Invalid event data") unless event.instance_of?(Projector::Event)
|
27
|
-
post(host, '/events', Projector::Envelope.new(
|
81
|
+
post(host, '/events', Projector::Envelope.new(event).to_hash).body
|
28
82
|
end
|
29
83
|
end
|
30
84
|
end
|
data/lib/projector/client.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
module Projector
|
2
2
|
require 'projector/transport/http'
|
3
3
|
require 'projector/error'
|
4
4
|
require 'projector/envelope'
|
@@ -6,11 +6,26 @@
|
|
6
6
|
require 'projector/event'
|
7
7
|
require 'projector/api_methods'
|
8
8
|
|
9
|
+
# Primary static class for interacting with the Projector API.
|
10
|
+
#
|
11
|
+
# The Projector SDK accepts direct configuration or uses environment variables, in support of the
|
12
|
+
# {http://12factor.net/ Twelve Factor Application} pattern. PROJECTOR_API_HOST and PROJECTOR_API_TOKEN
|
13
|
+
# variables are used by default.
|
14
|
+
#
|
15
|
+
# If you wish to manually configure the client, create an initializer and use the {Projector::Client.configure}
|
16
|
+
# block to set properties as needed.
|
17
|
+
#
|
18
|
+
# @example Manually configuring the Proejctor Client
|
19
|
+
# require 'projector'
|
20
|
+
# Projector::Client.configure do |client|
|
21
|
+
# client[:host] = 'https://api.projector.com'
|
22
|
+
# client[:token] = '<YOUR-API-TOKEN>'
|
23
|
+
# end
|
9
24
|
class Client
|
10
25
|
include Projector::ApiMethods
|
11
26
|
include Projector::Transport::HTTP
|
12
27
|
|
13
|
-
attr_accessor :
|
28
|
+
attr_accessor :token, :result_format
|
14
29
|
|
15
30
|
# The current configuration parameters for all clients
|
16
31
|
def self.options
|
@@ -19,19 +34,25 @@
|
|
19
34
|
}
|
20
35
|
end
|
21
36
|
|
22
|
-
#
|
37
|
+
# Replaces the global Projector client options
|
38
|
+
#
|
39
|
+
# @param [Hash] val the parameters to store
|
23
40
|
def self.options=(val)
|
24
41
|
@options = val
|
25
42
|
end
|
26
43
|
|
27
|
-
#
|
44
|
+
# Configure the global Projector client options using a block
|
45
|
+
#
|
46
|
+
# @yield [Hash] a hash of configuration values for the Projector Client
|
28
47
|
def self.configure
|
29
48
|
yield options
|
30
49
|
end
|
31
50
|
|
32
51
|
# Initialize a new client.
|
33
52
|
#
|
34
|
-
# @param
|
53
|
+
# @param [Hash] options
|
54
|
+
# @option options [String] :access_token The API access token
|
55
|
+
# @option options [String] :host The API host
|
35
56
|
def initialize(options = {})
|
36
57
|
options = self.class.options.merge(options)
|
37
58
|
@host = options[:host] || ENV['PROJECTOR_API_HOST']
|
@@ -39,8 +60,9 @@
|
|
39
60
|
@options = options
|
40
61
|
end
|
41
62
|
|
63
|
+
# The Projector API Host
|
42
64
|
def host
|
43
|
-
|
65
|
+
@options[:host]
|
44
66
|
end
|
45
67
|
end
|
46
68
|
end
|
data/lib/projector/end_user.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
module Projector
|
2
|
+
# Internally represents an End User in your application.
|
2
3
|
class EndUser
|
3
|
-
|
4
|
-
SCHEMA = 'http://schemas.projector.com/schemas/com.projector/server_api/jsonschema/0-9-2#/definitions/end_user_post'
|
5
|
-
|
6
4
|
attr_accessor :id, :tags
|
7
5
|
|
6
|
+
# @param [Hash] params Properties for a user
|
7
|
+
# @option params [String] :id the id of the user in your application
|
8
|
+
# @option params [Array] :tags an array of tags to apply to the user
|
8
9
|
def initialize(params = {})
|
9
10
|
raise Projector::InvalidEndUser.new("Invalid end user, id missing") if params[:id].nil?
|
10
11
|
if params.instance_of?(Hash)
|
@@ -16,6 +17,8 @@ module Projector
|
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
20
|
+
# Serializes the EndUser object to the correct Projector-defined hash format.
|
21
|
+
# @return [Hash]
|
19
22
|
def to_hash
|
20
23
|
basic_hash = {
|
21
24
|
id: self.id.to_s,
|
data/lib/projector/envelope.rb
CHANGED
@@ -1,22 +1,25 @@
|
|
1
1
|
module Projector
|
2
|
+
# Wraps all requests to the Projector API in an envelope specifying data as an explicit parameter.
|
2
3
|
class Envelope
|
3
4
|
attr_accessor :schema, :data
|
4
5
|
|
5
|
-
|
6
|
-
|
6
|
+
# @param [Object] data the data hash or array containing parameters for the API call
|
7
|
+
def initialize(data)
|
7
8
|
@data = data
|
8
9
|
end
|
9
10
|
|
11
|
+
# Returns the payload of the envelope
|
12
|
+
# @return [Object]
|
10
13
|
def payload
|
11
14
|
data.respond_to?(:to_hash) ? data.to_hash : data
|
12
15
|
end
|
13
16
|
|
17
|
+
# Serializes the envelope to the correct Projector-defined envelope hash format
|
14
18
|
def to_hash(params = {})
|
15
|
-
raise Projector::InvalidEnvelope unless @data
|
19
|
+
raise Projector::InvalidEnvelope unless @data
|
16
20
|
{
|
17
|
-
schema: schema,
|
18
21
|
data: payload
|
19
22
|
}
|
20
23
|
end
|
21
24
|
end
|
22
|
-
end
|
25
|
+
end
|
data/lib/projector/error.rb
CHANGED
@@ -1,61 +1,57 @@
|
|
1
1
|
module Projector
|
2
|
-
# Standard Projector error
|
2
|
+
# Standard Projector error.
|
3
3
|
class Error < StandardError
|
4
|
-
|
4
|
+
# The request object for the request
|
5
|
+
attr_accessor :request
|
6
|
+
# The response object (if present)
|
7
|
+
attr_accessor :response
|
8
|
+
# The http response body, if present
|
9
|
+
attr_accessor :response_body
|
10
|
+
# The Projector API request id
|
11
|
+
attr_accessor :request_id
|
5
12
|
end
|
6
13
|
|
7
|
-
# Raised when Projector returns a 400 HTTP status code
|
14
|
+
# Raised when Projector returns a 400 HTTP status code.
|
8
15
|
class BadRequest < Error; end
|
9
16
|
|
10
|
-
# Raised when Projector returns a 401 HTTP status code
|
17
|
+
# Raised when Projector returns a 401 HTTP status code.
|
11
18
|
class Unauthorized < Error; end
|
12
19
|
|
13
|
-
# Raised when Projector returns a 403 HTTP status code
|
20
|
+
# Raised when Projector returns a 403 HTTP status code.
|
14
21
|
class Forbidden < Error; end
|
15
22
|
|
16
|
-
# Raised when Projector returns a 404 HTTP status code
|
23
|
+
# Raised when Projector returns a 404 HTTP status code.
|
17
24
|
class NotFound < Error; end
|
18
25
|
|
19
|
-
# Raised when Projector returns a 406 HTTP status code
|
26
|
+
# Raised when Projector returns a 406 HTTP status code.
|
20
27
|
class NotAcceptable < Error; end
|
21
28
|
|
22
|
-
# Raised when Projector returns a 422 HTTP status code
|
29
|
+
# Raised when Projector returns a 422 HTTP status code.
|
23
30
|
class UnprocessableEntity < Error; end
|
24
31
|
|
25
|
-
# Raised when Projector returns a 500 HTTP status code
|
32
|
+
# Raised when Projector returns a 500 HTTP status code.
|
26
33
|
class InternalServerError < Error;
|
27
34
|
attr_accessor :retry_after
|
28
35
|
end
|
29
36
|
|
30
|
-
# Raised when Projector returns a 501 HTTP status code
|
37
|
+
# Raised when Projector returns a 501 HTTP status code.
|
31
38
|
class NotImplemented < Error; end
|
32
39
|
|
33
|
-
# Raised when Projector returns a 502 HTTP status code
|
40
|
+
# Raised when Projector returns a 502 HTTP status code.
|
34
41
|
class BadGateway < Error; end
|
35
42
|
|
36
|
-
# Raised when Projector returns a 503 HTTP status code
|
43
|
+
# Raised when Projector returns a 503 HTTP status code.
|
37
44
|
class ServiceUnavailable < Error; end
|
38
45
|
|
39
|
-
# Raised when a unique ID is required but not provided
|
46
|
+
# Raised when a unique ID is required but not provided.
|
40
47
|
class UniqueIDRequired < Error; end
|
41
48
|
|
49
|
+
# Raised when an invalid End User is sumbitted to the Projector API.
|
42
50
|
class InvalidEndUser < StandardError; end
|
51
|
+
|
52
|
+
# Raised when an invalid event is submitted to the Projector API.
|
43
53
|
class InvalidEvent < StandardError; end
|
44
|
-
class InvalidDevice < StandardError; end
|
45
|
-
class InvalidRegistration < StandardError; end
|
46
|
-
class InvalidEnvelope < StandardError; end
|
47
54
|
|
48
|
-
#
|
49
|
-
|
50
|
-
400 => Projector::BadRequest,
|
51
|
-
401 => Projector::Unauthorized,
|
52
|
-
403 => Projector::Forbidden,
|
53
|
-
404 => Projector::NotFound,
|
54
|
-
406 => Projector::NotAcceptable,
|
55
|
-
422 => Projector::UnprocessableEntity,
|
56
|
-
500 => Projector::InternalServerError,
|
57
|
-
501 => Projector::InternalServerError,
|
58
|
-
502 => Projector::InternalServerError,
|
59
|
-
503 => Projector::InternalServerError
|
60
|
-
}
|
55
|
+
# Raised when an invalid envelope is submitted to the Projector API.
|
56
|
+
class InvalidEnvelope < StandardError; end
|
61
57
|
end
|
data/lib/projector/event.rb
CHANGED
@@ -1,13 +1,20 @@
|
|
1
1
|
module Projector
|
2
2
|
require 'securerandom'
|
3
3
|
|
4
|
+
# Internally represents an event being sent to the Projector API.
|
4
5
|
class Event
|
5
|
-
|
6
|
-
|
6
|
+
|
7
|
+
# @param [Hash] params a hash of properties to send with the event
|
8
|
+
# @option params [String] id (SecureRandom.uuid) the unique identifier for this event in your system
|
9
|
+
# @option params [Number] event_time (Time.now) the ms since epoch for this event
|
10
|
+
# @option params [Hash] event_context data providing context for the event. Used in delivery rules, templates, and more at Projector.
|
11
|
+
# @option params [Hash] target containing either an *end_users* or *tags* key and a corresponding hash of values.
|
7
12
|
def initialize(params)
|
8
13
|
@params = params.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
9
14
|
end
|
10
15
|
|
16
|
+
# Serializes the Event object to the correct Projector-defined hash format.
|
17
|
+
# @return [Hash]
|
11
18
|
def to_hash
|
12
19
|
overrides = {
|
13
20
|
id: @params[:id] ? @params[:id].to_s : SecureRandom.uuid,
|
@@ -1,13 +1,31 @@
|
|
1
1
|
module Projector
|
2
|
+
# Abstracted network transports for communicating to the Projector API.
|
3
|
+
# @api private
|
2
4
|
module Transport
|
3
|
-
# Persistent HTTP API transport adapter
|
5
|
+
# Persistent HTTP API transport adapter.
|
4
6
|
module HTTP
|
5
7
|
require 'net/http/persistent'
|
6
8
|
require 'uri'
|
7
9
|
require 'json'
|
10
|
+
require 'projector/error'
|
8
11
|
|
12
|
+
# Maximum URI redrects to follow before failing.
|
9
13
|
MAX_REDIRECTS = 2
|
10
14
|
|
15
|
+
# Status code to exception map.
|
16
|
+
ERROR_MAP = {
|
17
|
+
400 => Projector::BadRequest,
|
18
|
+
401 => Projector::Unauthorized,
|
19
|
+
403 => Projector::Forbidden,
|
20
|
+
404 => Projector::NotFound,
|
21
|
+
406 => Projector::NotAcceptable,
|
22
|
+
422 => Projector::UnprocessableEntity,
|
23
|
+
500 => Projector::InternalServerError,
|
24
|
+
501 => Projector::InternalServerError,
|
25
|
+
502 => Projector::InternalServerError,
|
26
|
+
503 => Projector::InternalServerError
|
27
|
+
}
|
28
|
+
|
11
29
|
[:get, :post, :put, :delete, :patch].each do |method|
|
12
30
|
define_method method do |*args|
|
13
31
|
json_request(method, *args)
|
@@ -16,6 +34,13 @@ module Projector
|
|
16
34
|
|
17
35
|
protected
|
18
36
|
|
37
|
+
# Makes an HTTP Request
|
38
|
+
#
|
39
|
+
# @param [String] method The HTTP method to use
|
40
|
+
# @param [String] host the host to connect to
|
41
|
+
# @param [String] path the path to request
|
42
|
+
# @param [Hash] params (nil) the parameters to send
|
43
|
+
# @param [Integer] redirect_count (0) the number of redirects that have been followed so far
|
19
44
|
def request(method, host, path, params = nil, redirect_count = 0)
|
20
45
|
uri = URI.parse("#{host}#{path}")
|
21
46
|
|
@@ -73,6 +98,8 @@ module Projector
|
|
73
98
|
response
|
74
99
|
end
|
75
100
|
|
101
|
+
# Returns an instance of the HTTP client
|
102
|
+
# @return [Net::HTTP::Persistent]
|
76
103
|
def http
|
77
104
|
@http ||= begin
|
78
105
|
http = Net::HTTP::Persistent.new('projector')
|
@@ -82,6 +109,9 @@ module Projector
|
|
82
109
|
end
|
83
110
|
end
|
84
111
|
|
112
|
+
# Constructs a request object based off the method
|
113
|
+
# @param [Symbol] method the HTTP method
|
114
|
+
# @param [String] uri the HTTP URI to request
|
85
115
|
def build_request(method, uri)
|
86
116
|
case method
|
87
117
|
when :get
|
@@ -97,6 +127,9 @@ module Projector
|
|
97
127
|
end
|
98
128
|
end
|
99
129
|
|
130
|
+
# Converts a hash to HTTP URI format
|
131
|
+
# @param [Hash] hash the hash to be converted
|
132
|
+
# @return [String]
|
100
133
|
def to_url_params(hash)
|
101
134
|
params = []
|
102
135
|
hash.each_pair do |key, value|
|
@@ -105,6 +138,7 @@ module Projector
|
|
105
138
|
params.sort.join('&')
|
106
139
|
end
|
107
140
|
|
141
|
+
# Escapes params to a URI-safe form
|
108
142
|
def param_for(key, value, parent = nil)
|
109
143
|
if value.is_a?(Hash)
|
110
144
|
params = []
|
@@ -118,12 +152,14 @@ module Projector
|
|
118
152
|
end
|
119
153
|
end
|
120
154
|
|
155
|
+
# Raises the appropriate error based on the {ERROR_MAP}
|
156
|
+
# raise [Projector::Error]
|
121
157
|
def handle_error(request, response)
|
122
|
-
return unless error =
|
158
|
+
return unless error = ERROR_MAP[response.code.to_i]
|
123
159
|
message = nil
|
124
160
|
begin
|
125
161
|
message = JSON.parse(response.body) || request.path
|
126
|
-
rescue JSON::ParserError
|
162
|
+
rescue JSON::ParserError
|
127
163
|
end
|
128
164
|
# Raise error
|
129
165
|
err = error.new(message)
|
@@ -135,20 +171,27 @@ module Projector
|
|
135
171
|
raise err
|
136
172
|
end
|
137
173
|
|
174
|
+
# Makes a request and parses the result as a JSON object
|
175
|
+
# @return [Response]
|
138
176
|
def json_request(*args)
|
139
177
|
# Perform request, pass result format
|
140
178
|
Response.new(request(*args))
|
141
179
|
end
|
142
180
|
|
181
|
+
# Returns true if the request is not an error or redirect
|
182
|
+
# @return [Boolean]
|
143
183
|
def boolean_from_response(*args)
|
144
184
|
response = request(*args)
|
145
185
|
(200..299).include? response.code.to_i
|
146
186
|
end
|
147
187
|
|
188
|
+
# Returns true if the HTTP method can post data
|
189
|
+
# @return [Boolean]
|
148
190
|
def can_post_data?(method)
|
149
191
|
[:post, :put, :patch].include?(method)
|
150
192
|
end
|
151
193
|
|
194
|
+
# Logs all HTTP request data to STDOUT if the PROJECTOR_DEBUG_HTTP env variable is set
|
152
195
|
def log_debug_data(*args)
|
153
196
|
return unless ENV['PROJECTOR_DEBUG_HTTP']
|
154
197
|
@logger ||= begin
|
@@ -190,14 +233,16 @@ module Projector
|
|
190
233
|
# Parse JSON
|
191
234
|
begin
|
192
235
|
self.body = JSON.parse(response_body)
|
193
|
-
rescue JSON::ParserError
|
236
|
+
rescue JSON::ParserError
|
194
237
|
self.body = response_body
|
195
238
|
end
|
196
239
|
end
|
197
240
|
end
|
198
241
|
|
199
|
-
|
200
|
-
|
242
|
+
# Raised when the HTTP client follows too many HTTP redirects
|
243
|
+
class TooManyRedirectsError < Projector::Error
|
244
|
+
# The redirect count that caused this error
|
245
|
+
attr_accessor :redirect_count
|
201
246
|
end
|
202
247
|
end
|
203
248
|
end
|
data/lib/projector/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: projector
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Projector
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-07-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: net-http-persistent
|
@@ -38,8 +38,10 @@ extensions: []
|
|
38
38
|
extra_rdoc_files: []
|
39
39
|
files:
|
40
40
|
- ".gitignore"
|
41
|
+
- ".yardopts"
|
41
42
|
- Gemfile
|
42
43
|
- LICENSE
|
44
|
+
- Overview.md
|
43
45
|
- README.md
|
44
46
|
- Rakefile
|
45
47
|
- bin/console
|
@@ -81,9 +83,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
83
|
version: '0'
|
82
84
|
requirements: []
|
83
85
|
rubyforge_project:
|
84
|
-
rubygems_version: 2.
|
86
|
+
rubygems_version: 2.5.1
|
85
87
|
signing_key:
|
86
88
|
specification_version: 4
|
87
89
|
summary: Ruby SDK for interacting with the Projector SDK
|
88
90
|
test_files: []
|
89
|
-
has_rdoc:
|