castle-rb 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +2 -5
  2. data/README.md +1 -1
  3. data/lib/castle-rb.rb +2 -19
  4. data/lib/castle.rb +42 -0
  5. data/lib/castle/api.rb +48 -0
  6. data/lib/castle/client.rb +43 -0
  7. data/lib/castle/configuration.rb +39 -0
  8. data/lib/{castle-rb/support → castle}/cookie_store.rb +9 -5
  9. data/lib/castle/errors.rb +27 -0
  10. data/lib/castle/extractors/client_id.rb +28 -0
  11. data/lib/castle/extractors/headers.rb +36 -0
  12. data/lib/castle/extractors/ip.rb +16 -0
  13. data/lib/castle/headers.rb +39 -0
  14. data/lib/castle/request.rb +34 -0
  15. data/lib/castle/response.rb +43 -0
  16. data/lib/castle/support.rb +11 -0
  17. data/lib/{castle-rb → castle}/support/padrino.rb +1 -1
  18. data/lib/{castle-rb → castle}/support/rails.rb +2 -0
  19. data/lib/{castle-rb → castle}/support/sinatra.rb +1 -1
  20. data/lib/castle/system.rb +36 -0
  21. data/lib/castle/version.rb +5 -0
  22. data/spec/lib/castle/api_spec.rb +54 -0
  23. data/spec/lib/castle/client_spec.rb +64 -0
  24. data/spec/lib/castle/configuration_spec.rb +98 -0
  25. data/spec/lib/castle/extractors/client_id_spec.rb +39 -0
  26. data/spec/lib/castle/extractors/headers_spec.rb +25 -0
  27. data/spec/lib/castle/extractors/ip_spec.rb +20 -0
  28. data/spec/lib/castle/headers_spec.rb +82 -0
  29. data/spec/lib/castle/request_spec.rb +37 -0
  30. data/spec/lib/castle/response_spec.rb +71 -0
  31. data/spec/lib/castle/system_spec.rb +70 -0
  32. data/spec/lib/castle/version_spec.rb +9 -0
  33. data/spec/lib/castle_spec.rb +73 -0
  34. data/spec/spec_helper.rb +9 -3
  35. metadata +49 -76
  36. data/lib/castle-rb/api.rb +0 -94
  37. data/lib/castle-rb/client.rb +0 -67
  38. data/lib/castle-rb/configuration.rb +0 -48
  39. data/lib/castle-rb/errors.rb +0 -15
  40. data/lib/castle-rb/version.rb +0 -3
  41. data/spec/api_spec.rb +0 -31
  42. data/spec/client_spec.rb +0 -55
data/lib/castle-rb/api.rb DELETED
@@ -1,94 +0,0 @@
1
- module Castle
2
- class API
3
- attr_accessor :http, :headers
4
-
5
- def initialize(cookie_id, ip, headers)
6
- @http = Net::HTTP.new(Castle.config.api_endpoint.host,
7
- Castle.config.api_endpoint.port)
8
-
9
- @http.read_timeout = Castle.config.request_timeout
10
-
11
- if Castle.config.api_endpoint.scheme == 'https'
12
- @http.use_ssl = true
13
- @http.verify_mode = OpenSSL::SSL::VERIFY_PEER
14
- end
15
-
16
- @headers = {
17
- "Content-Type" => "application/json",
18
- "X-Castle-Cookie-Id" => cookie_id,
19
- "X-Castle-Ip" => ip,
20
- "X-Castle-Headers" => headers,
21
- "X-Castle-Client-User-Agent" => JSON.generate(client_user_agent),
22
- "X-Castle-Source" => Castle.config.source_header,
23
- "User-Agent" => "Castle/v1 RubyBindings/#{Castle::VERSION}"
24
- }
25
-
26
- @headers.delete_if { |k, v| v.nil? }
27
- end
28
-
29
- def request(endpoint, args, method: :post)
30
- http_method = method.to_s.capitalize
31
- req = Net::HTTP.const_get(http_method).new(
32
- "#{Castle.config.api_endpoint.path}/#{endpoint}", @headers)
33
- req.basic_auth("", Castle.config.api_secret)
34
- req.body = args.to_json unless http_method == 'Get'
35
-
36
- begin
37
- response = @http.request(req)
38
- rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError,
39
- Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
40
- Net::ProtocolError, Net::ReadTimeout => e
41
- raise Castle::RequestError, 'Castle API connection error'
42
- end
43
-
44
- case response.code.to_i
45
- when 200..299
46
- # OK
47
- when 400
48
- raise Castle::BadRequestError, response[:message]
49
- when 401
50
- raise Castle::UnauthorizedError, response[:message]
51
- when 403
52
- raise Castle::ForbiddenError, response[:message]
53
- when 404
54
- raise Castle::NotFoundError, response[:message]
55
- when 419
56
- raise Castle::UserUnauthorizedError, response[:message]
57
- when 422
58
- raise Castle::InvalidParametersError, response[:message]
59
- else
60
- raise Castle::ApiError, response[:message]
61
- end
62
-
63
- if response.body.nil? || response.body.empty?
64
- {}
65
- else
66
- begin
67
- JSON.parse(response.body, :symbolize_names => true)
68
- rescue JSON::ParserError
69
- raise Castle::ApiError, 'Invalid response from Castle API'
70
- end
71
- end
72
- end
73
-
74
- def client_user_agent
75
- @uname ||= get_uname
76
- lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
77
-
78
- {
79
- :bindings_version => Castle::VERSION,
80
- :lang => 'ruby',
81
- :lang_version => lang_version,
82
- :platform => RUBY_PLATFORM,
83
- :publisher => 'castle',
84
- :uname => @uname
85
- }
86
- end
87
-
88
- def get_uname
89
- `uname -a 2>/dev/null`.strip if RUBY_PLATFORM =~ /linux|darwin/i
90
- rescue Errno::ENOMEM # couldn't create subprocess
91
- "uname lookup failed"
92
- end
93
- end
94
- end
@@ -1,67 +0,0 @@
1
- module Castle
2
- class Client
3
- attr_accessor :do_not_track, :api
4
-
5
- def initialize(request, response)
6
- cookie_id = extract_cookie(request, response)['__cid'] || ''
7
- ip = request.ip
8
- headers = header_string(request)
9
-
10
- @api = API.new(cookie_id, ip, headers)
11
- end
12
-
13
- def fetch_review(id)
14
- @api.request("reviews/#{id}", nil, method: :get)
15
- end
16
-
17
- def identify(args)
18
- @api.request('identify', args) unless do_not_track?
19
- end
20
-
21
- def authenticate(args)
22
- @api.request('authenticate', args)
23
- end
24
-
25
- def track(args)
26
- @api.request('track', args) unless do_not_track?
27
- end
28
-
29
- def do_not_track!
30
- @do_not_track = true
31
- end
32
-
33
- def track!
34
- @do_not_track = false
35
- end
36
-
37
- def do_not_track?
38
- !!@do_not_track
39
- end
40
-
41
-
42
- private
43
-
44
- # Extract the cookie set by the Castle Javascript
45
- def extract_cookie(request, response)
46
- if response.class.name == 'ActionDispatch::Cookies::CookieJar'
47
- Castle::CookieStore::Rack.new(response)
48
- else
49
- Castle::CookieStore::Base.new(request, response)
50
- end
51
- end
52
-
53
- # Serialize HTTP headers
54
- def header_string(request)
55
- scrub_headers = ['Cookie']
56
-
57
- headers = request.env.keys.grep(/^HTTP_/).map do |header|
58
- name = header.gsub(/^HTTP_/, '').split('_').map(&:capitalize).join('-')
59
- unless scrub_headers.include?(name)
60
- { name => request.env[header] }
61
- end
62
- end.compact.inject(:merge)
63
-
64
- JSON.generate(headers || {})
65
- end
66
- end
67
- end
@@ -1,48 +0,0 @@
1
- module Castle
2
- class << self
3
- def configure(config_hash=nil)
4
- if config_hash
5
- config_hash.each do |k,v|
6
- config.send("#{k}=", v)
7
- end
8
- end
9
-
10
- yield(config) if block_given?
11
- end
12
-
13
- def config
14
- @configuration ||= Castle::Configuration.new
15
- end
16
-
17
- def api_secret=(api_secret)
18
- config.api_secret = api_secret
19
- end
20
- end
21
-
22
- class Configuration
23
- attr_accessor :request_timeout
24
- attr_accessor :source_header
25
-
26
- def initialize
27
- self.request_timeout = 30.0
28
- self.api_endpoint =
29
- ENV['CASTLE_API_ENDPOINT'] || 'https://api.castle.io/v1'
30
- end
31
-
32
- def api_secret
33
- ENV['CASTLE_API_SECRET'] || @_api_secret || ''
34
- end
35
-
36
- def api_secret=(value)
37
- @_api_secret = value
38
- end
39
-
40
- def api_endpoint
41
- @_api_endpoint
42
- end
43
-
44
- def api_endpoint=(value)
45
- @_api_endpoint = URI(value)
46
- end
47
- end
48
- end
@@ -1,15 +0,0 @@
1
- class Castle::Error < Exception; end
2
-
3
- class Castle::RequestError < Castle::Error; end
4
- class Castle::SecurityError < Castle::Error; end
5
- class Castle::ConfigurationError < Castle::Error; end
6
-
7
- class Castle::ApiError < Castle::Error; end
8
-
9
- class Castle::BadRequestError < Castle::ApiError; end
10
- class Castle::ForbiddenError < Castle::ApiError; end
11
- class Castle::NotFoundError < Castle::ApiError; end
12
- class Castle::UserUnauthorizedError < Castle::ApiError; end
13
- class Castle::InvalidParametersError < Castle::ApiError; end
14
-
15
- class Castle::UnauthorizedError < Castle::ApiError; end
@@ -1,3 +0,0 @@
1
- module Castle
2
- VERSION = "2.2.0"
3
- end
data/spec/api_spec.rb DELETED
@@ -1,31 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Castle::API do
4
- let(:api) { Castle::API.new('abcd', '1.2.3.4', '{}') }
5
-
6
- it 'handles timeout' do
7
- stub_request(:any, /api.castle.io/).to_timeout
8
- expect {
9
- api.request('authenticate', user_id: '1234')
10
- }.to raise_error(Castle::RequestError)
11
- end
12
-
13
- it 'handles non-OK response code' do
14
- stub_request(:any, /api.castle.io/).to_return(status: 400)
15
- expect {
16
- api.request('authenticate', user_id: '1234')
17
- }.to raise_error(Castle::BadRequestError)
18
- end
19
-
20
- it 'handles custom API endpoint' do
21
- stub_request(:any, /new.herokuapp.com/)
22
-
23
- api_endpoint = 'http://new.herokuapp.com:3000/v2'
24
- Castle.config.api_endpoint = api_endpoint
25
- uri = URI(api_endpoint)
26
-
27
- api.request('authenticate', user_id: '1234')
28
- assert_requested :post,
29
- "#{api_endpoint.gsub(/new/, ":secret@new")}/authenticate", times: 1
30
- end
31
- end
data/spec/client_spec.rb DELETED
@@ -1,55 +0,0 @@
1
- require 'spec_helper'
2
-
3
- class Request < Rack::Request
4
- def delegate?; false; end
5
- end
6
-
7
- describe Castle::Client do
8
- let(:ip) {'1.2.3.4' }
9
- let(:cookie_id) { 'abcd' }
10
- let(:env) do
11
- Rack::MockRequest.env_for('/',
12
- 'HTTP_X_FORWARDED_FOR' => '1.2.3.4',
13
- 'HTTP_COOKIE' => "__cid=#{cookie_id};other=efgh"
14
- )
15
- end
16
- let(:request) { Request.new(env) }
17
- let(:client) { Castle::Client.new(request, nil) }
18
- let(:review_id) { '12356789' }
19
-
20
- it 'parses the request' do
21
- expect(Castle::API).to receive(:new).with(cookie_id, ip,
22
- "{\"X-Forwarded-For\":\"#{ip}\"}").and_call_original
23
-
24
- client = Castle::Client.new(request, nil)
25
- client.authenticate({name: '$login.succeeded', user_id: '1234'})
26
- end
27
-
28
- it 'identifies' do
29
- client.identify( user_id: '1234', traits: { name: 'Jo' })
30
- assert_requested :post, 'https://:secret@api.castle.io/v1/identify',
31
- times: 1,
32
- body: { user_id: '1234', traits: { name: 'Jo' } }
33
- end
34
-
35
- it 'authenticates' do
36
- client.authenticate(name: '$login.succeeded', user_id: '1234')
37
- assert_requested :post, 'https://:secret@api.castle.io/v1/authenticate',
38
- times: 1,
39
- body: { name: '$login.succeeded', user_id: "1234" }
40
- end
41
-
42
- it 'tracks' do
43
- client.track(name: '$login.succeeded', user_id: '1234')
44
- assert_requested :post, 'https://:secret@api.castle.io/v1/track',
45
- times: 1,
46
- body: { name: '$login.succeeded', user_id: "1234" }
47
- end
48
-
49
- it 'fetches review' do
50
- client.fetch_review(review_id)
51
-
52
- assert_requested :get, "https://:secret@api.castle.io/v1/reviews/#{review_id}",
53
- times: 1
54
- end
55
- end