bixby-common 0.3.16 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 53b5ec30f44a5f248d360a13d02bc46881e64f03
4
+ data.tar.gz: 4ad3d8400f6390ce4630a502e15e4fc9ca5d4507
5
+ SHA512:
6
+ metadata.gz: db1b6968efb742cf556c6cfdeb4a0836a05869d5088abc126c5360d2e8d8494a209bd261d9bac3b5fdfb18fee17ce775b7d123bdd7a02149bd037970877726ef
7
+ data.tar.gz: 0ca4ee9520635bc97adea27f4c293b4a4797958a296eaf87d3d9d016618ff43c5db197522e99baf506cec46861001f4f5ec1233cc42787c9d79407084024fb2b
data/Gemfile CHANGED
@@ -1,5 +1,7 @@
1
1
  source "https://rubygems.org"
2
2
 
3
+ gem "faye-websocket"
4
+ gem "api-auth", :git => "https://github.com/chetan/api_auth.git", :branch => "bixby"
3
5
  gem "multi_json"
4
6
  gem "httpi", :git => "https://github.com/chetan/httpi.git",
5
7
  :branch => "chunked_responses"
data/Gemfile.lock CHANGED
@@ -1,3 +1,10 @@
1
+ GIT
2
+ remote: https://github.com/chetan/api_auth.git
3
+ revision: 6d9351de901a51d6a386d547a8d32e23c4314550
4
+ branch: bixby
5
+ specs:
6
+ api-auth (1.0.3)
7
+
1
8
  GIT
2
9
  remote: https://github.com/chetan/httpi.git
3
10
  revision: f6e49303fee6778c714bcc556fa5f9247bc3a3f3
@@ -8,12 +15,16 @@ GIT
8
15
 
9
16
  GIT
10
17
  remote: https://github.com/chetan/jeweler.git
11
- revision: 41062abfc55dc1f5775d6419c796fd1c8756fc7b
18
+ revision: 9b8b2376c79299b823a5e9aafae3d647df943b6f
12
19
  branch: bixby
13
20
  specs:
14
- jeweler (1.8.4)
21
+ jeweler (1.8.7)
22
+ builder
15
23
  bundler (~> 1.0)
16
24
  git (>= 1.2.5)
25
+ github_api (= 0.10.1)
26
+ highline (>= 1.6.15)
27
+ nokogiri (= 1.5.10)
17
28
  rake
18
29
  rdoc
19
30
 
@@ -28,14 +39,14 @@ GIT
28
39
 
29
40
  GIT
30
41
  remote: https://github.com/chetan/test_guard.git
31
- revision: bbc871053ca91021eea412122035a9427748f8a5
42
+ revision: 7d069b2d7e46fcbecd7a52966fda7d2d1945e2c9
32
43
  specs:
33
- test_guard (0.2.0)
44
+ test_guard (0.2.1)
34
45
  awesome_print
35
46
  debugger
36
47
  debugger-pry
37
48
  growl
38
- guard
49
+ listen
39
50
  simplecov
40
51
  simplecov-console
41
52
  single_test
@@ -48,6 +59,7 @@ GEM
48
59
  ansi (1.4.3)
49
60
  awesome_print (1.1.0)
50
61
  bouncy-castle-java (1.5.0147)
62
+ builder (3.2.2)
51
63
  coderay (1.0.9)
52
64
  colorize (0.5.8)
53
65
  columnize (0.3.6)
@@ -63,24 +75,36 @@ GEM
63
75
  debugger (~> 1)
64
76
  pry (>= 0.9.9)
65
77
  debugger-ruby_core_source (1.2.3)
78
+ eventmachine (1.0.3)
79
+ eventmachine (1.0.3-java)
80
+ faraday (0.8.8)
81
+ multipart-post (~> 1.2.0)
82
+ faye-websocket (0.6.3)
83
+ eventmachine (>= 0.12.0)
84
+ websocket-driver (>= 0.2.0)
66
85
  ffi (1.9.0)
67
86
  ffi (1.9.0-java)
68
- formatador (0.2.4)
69
- git (1.2.5)
87
+ git (1.2.6)
88
+ github_api (0.10.1)
89
+ addressable
90
+ faraday (~> 0.8.1)
91
+ hashie (>= 1.2)
92
+ multi_json (~> 1.4)
93
+ nokogiri (~> 1.5.2)
94
+ oauth2
70
95
  growl (1.0.3)
71
- guard (1.8.2)
72
- formatador (>= 0.2.4)
73
- listen (>= 1.0.0)
74
- lumberjack (>= 1.0.2)
75
- pry (>= 0.9.10)
76
- thor (>= 0.14.6)
96
+ hashie (2.0.5)
97
+ highline (1.6.19)
77
98
  hirb (0.7.1)
99
+ httpauth (0.2.0)
78
100
  httpclient (2.3.4.1)
79
101
  jruby-openssl (0.8.8)
80
102
  bouncy-castle-java (>= 1.5.0147)
81
103
  json (1.8.0)
82
104
  json (1.8.0-java)
83
- listen (1.2.3)
105
+ jwt (0.1.8)
106
+ multi_json (>= 1.5)
107
+ listen (1.3.0)
84
108
  rb-fsevent (>= 0.9.3)
85
109
  rb-inotify (>= 0.9)
86
110
  rb-kqueue (>= 0.2)
@@ -88,13 +112,23 @@ GEM
88
112
  logging (1.8.1)
89
113
  little-plugger (>= 1.1.3)
90
114
  multi_json (>= 1.3.6)
91
- lumberjack (1.0.4)
92
115
  metaclass (0.0.1)
93
116
  method_source (0.8.2)
94
117
  minitest (4.7.5)
95
118
  mocha (0.14.0)
96
119
  metaclass (~> 0.0.1)
97
120
  multi_json (1.7.9)
121
+ multi_xml (0.5.5)
122
+ multipart-post (1.2.0)
123
+ nokogiri (1.5.10)
124
+ nokogiri (1.5.10-java)
125
+ oauth2 (0.9.2)
126
+ faraday (~> 0.8)
127
+ httpauth (~> 0.2)
128
+ jwt (~> 0.1.4)
129
+ multi_json (~> 1.0)
130
+ multi_xml (~> 0.5)
131
+ rack (~> 1.2)
98
132
  oj (2.1.4)
99
133
  pry (0.9.12.2)
100
134
  coderay (~> 1.0.5)
@@ -123,18 +157,19 @@ GEM
123
157
  hirb
124
158
  simplecov
125
159
  simplecov-html (0.7.1)
126
- single_test (0.5.2)
127
- rake (> 0.9)
160
+ single_test (0.6.0)
161
+ rake
128
162
  slop (3.4.6)
129
163
  spoon (0.0.4)
130
164
  ffi
131
165
  test-unit (2.5.5)
132
- thor (0.18.1)
133
166
  turn (0.9.6)
134
167
  ansi
135
168
  webmock (1.13.0)
136
169
  addressable (>= 2.2.7)
137
170
  crack (>= 0.3.2)
171
+ websocket-driver (0.2.3)
172
+ websocket-driver (0.2.3-java)
138
173
  yard (0.8.7)
139
174
 
140
175
  PLATFORMS
@@ -142,8 +177,10 @@ PLATFORMS
142
177
  ruby
143
178
 
144
179
  DEPENDENCIES
180
+ api-auth!
145
181
  bundler (~> 1.1)
146
182
  curb
183
+ faye-websocket
147
184
  httpclient
148
185
  httpi!
149
186
  jeweler!
data/Rakefile CHANGED
@@ -47,8 +47,7 @@ end
47
47
  task :default => :test
48
48
 
49
49
  begin
50
- require 'single_test'
51
- SingleTest.load_tasks
50
+ require 'single_test/tasks'
52
51
 
53
52
  rescue LoadError
54
53
  warn "single_test not available"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.16
1
+ 0.4.0
data/bixby-common.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "bixby-common"
8
- s.version = "0.3.16"
8
+ s.version = "0.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Chetan Sarva"]
12
- s.date = "2013-08-13"
12
+ s.date = "2013-08-24"
13
13
  s.description = "Bixby Common files/libs"
14
14
  s.email = "chetan@pixelcop.net"
15
15
  s.extra_rdoc_files = [
@@ -24,8 +24,13 @@ Gem::Specification.new do |s|
24
24
  "VERSION",
25
25
  "bixby-common.gemspec",
26
26
  "lib/bixby-common.rb",
27
+ "lib/bixby-common/api/api_channel.rb",
28
+ "lib/bixby-common/api/encrypted_json_request.rb",
29
+ "lib/bixby-common/api/http_channel.rb",
27
30
  "lib/bixby-common/api/json_request.rb",
28
31
  "lib/bixby-common/api/json_response.rb",
32
+ "lib/bixby-common/api/rpc_handler.rb",
33
+ "lib/bixby-common/api/signed_json_request.rb",
29
34
  "lib/bixby-common/bixby.rb",
30
35
  "lib/bixby-common/command_response.rb",
31
36
  "lib/bixby-common/command_spec.rb",
@@ -40,6 +45,12 @@ Gem::Specification.new do |s|
40
45
  "lib/bixby-common/util/jsonify.rb",
41
46
  "lib/bixby-common/util/log.rb",
42
47
  "lib/bixby-common/util/log/filtering_layout.rb",
48
+ "lib/bixby-common/websocket/api_channel.rb",
49
+ "lib/bixby-common/websocket/async_response.rb",
50
+ "lib/bixby-common/websocket/client.rb",
51
+ "lib/bixby-common/websocket/message.rb",
52
+ "lib/bixby-common/websocket/request.rb",
53
+ "lib/bixby-common/websocket/response.rb",
43
54
  "test/bixby_common_test.rb",
44
55
  "test/command_response_test.rb",
45
56
  "test/command_spec_test.rb",
@@ -55,13 +66,15 @@ Gem::Specification.new do |s|
55
66
  ]
56
67
  s.homepage = "http://github.com/chetan/bixby-common"
57
68
  s.require_paths = ["lib"]
58
- s.rubygems_version = "1.8.25"
69
+ s.rubygems_version = "2.0.3"
59
70
  s.summary = "Bixby Common"
60
71
 
61
72
  if s.respond_to? :specification_version then
62
- s.specification_version = 3
73
+ s.specification_version = 4
63
74
 
64
75
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
76
+ s.add_runtime_dependency(%q<faye-websocket>, [">= 0"])
77
+ s.add_runtime_dependency(%q<api-auth>, [">= 0"])
65
78
  s.add_runtime_dependency(%q<multi_json>, [">= 0"])
66
79
  s.add_runtime_dependency(%q<httpi>, [">= 0"])
67
80
  s.add_runtime_dependency(%q<logging>, [">= 0"])
@@ -86,6 +99,8 @@ Gem::Specification.new do |s|
86
99
  s.add_development_dependency(%q<curb>, [">= 0"])
87
100
  s.add_development_dependency(%q<jruby-openssl>, [">= 0"])
88
101
  else
102
+ s.add_dependency(%q<faye-websocket>, [">= 0"])
103
+ s.add_dependency(%q<api-auth>, [">= 0"])
89
104
  s.add_dependency(%q<multi_json>, [">= 0"])
90
105
  s.add_dependency(%q<httpi>, [">= 0"])
91
106
  s.add_dependency(%q<logging>, [">= 0"])
@@ -111,6 +126,8 @@ Gem::Specification.new do |s|
111
126
  s.add_dependency(%q<jruby-openssl>, [">= 0"])
112
127
  end
113
128
  else
129
+ s.add_dependency(%q<faye-websocket>, [">= 0"])
130
+ s.add_dependency(%q<api-auth>, [">= 0"])
114
131
  s.add_dependency(%q<multi_json>, [">= 0"])
115
132
  s.add_dependency(%q<httpi>, [">= 0"])
116
133
  s.add_dependency(%q<logging>, [">= 0"])
@@ -0,0 +1,20 @@
1
+
2
+ module Bixby
3
+
4
+ class APIChannel
5
+
6
+ include Bixby::Log
7
+ extend Bixby::Log
8
+
9
+ # Execute the given request
10
+ #
11
+ # @param [JsonRequest] json_request
12
+ #
13
+ # @return [JsonResponse] response
14
+ def execute(json_request)
15
+ raise NotImplementedError
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,19 @@
1
+
2
+ module Bixby
3
+ class EncryptedJsonRequest < JsonRequest
4
+
5
+ def initialize(json_request, private_key, public_key, uuid="master")
6
+ self.operation = json_request.operation
7
+ self.params = json_request.params
8
+
9
+ # encrypt
10
+ data = json_request.to_json
11
+ @body = Bixby::CryptoUtil.encrypt(data, uuid, public_key, private_key)
12
+ end
13
+
14
+ def to_wire
15
+ @body
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,72 @@
1
+
2
+ module Bixby
3
+
4
+ class HttpChannel < Bixby::APIChannel
5
+
6
+ def initialize(uri)
7
+ @uri = uri
8
+ end
9
+
10
+ # Execute the given request
11
+ #
12
+ # @param [JsonRequest] json_request
13
+ #
14
+ # @return [JsonResponse] response
15
+ def execute(json_request)
16
+ execute_internal(json_request)
17
+ end
18
+
19
+
20
+ # Execute a download request
21
+ # NOTE: This method is only available on the HTTP Channel.
22
+ #
23
+ # @param [JsonRequest] json_request
24
+ # @param [Block] block
25
+ #
26
+ # @return [JsonResponse] response
27
+ def execute_download(json_request, &block)
28
+ execute_internal(json_request, &block)
29
+ end
30
+
31
+
32
+ private
33
+
34
+ # Execute the request, optionally passing a block to handle the response
35
+ #
36
+ # @param [JsonRequest] json_request
37
+ # @param [Block] block
38
+ #
39
+ # @return [JsonResponse] response
40
+ def execute_internal(json_request, &block)
41
+
42
+ req = HTTPI::Request.new(:url => @uri, :body => json_request.to_wire)
43
+
44
+ # add in extra headers if we have a SignedJsonRequest (or anything which has additional headers)
45
+ if json_request.respond_to? :headers then
46
+ req.headers.merge!(json_request.headers)
47
+ end
48
+
49
+ if not req.headers.include? "Content-Type" then
50
+ # add content-type if not set
51
+ # may be set when creating a signed request
52
+ req.headers["Content-Type"] = "application/json"
53
+ end
54
+
55
+ if block then
56
+ # execute request with block
57
+ req.on_body(&block)
58
+ HTTPI.post(req)
59
+ return JsonResponse.new("success")
60
+
61
+ else
62
+ # execute normal req, and return parsed response
63
+ res = HTTPI.post(req).body
64
+ return JsonResponse.from_json(res)
65
+ end
66
+
67
+
68
+ end
69
+
70
+
71
+ end
72
+ end
@@ -32,5 +32,9 @@ class JsonRequest
32
32
  s.join("\n")
33
33
  end # :nocov:
34
34
 
35
+ def to_wire
36
+ MultiJson.dump(self)
37
+ end
38
+
35
39
  end # JsonRequest
36
40
  end # Bixby
@@ -78,6 +78,10 @@ class JsonResponse
78
78
  s.join("\n")
79
79
  end # :nocov:
80
80
 
81
+ def to_wire
82
+ MultiJson.dump(self)
83
+ end
84
+
81
85
  end # JsonResponse
82
86
 
83
87
  end # Bixby
@@ -0,0 +1,35 @@
1
+
2
+ module Bixby
3
+ class RpcHandler
4
+
5
+ # Handle a request
6
+ #
7
+ # @param [JsonRequest] request
8
+ #
9
+ # @return [JsonResponse] response
10
+ def handle(request)
11
+ raise NotImplementedError
12
+ end
13
+
14
+ # Channel connect event
15
+ #
16
+ # NOTE: only used by WebSocket channels and generally only implemented by
17
+ # the server-side.
18
+ #
19
+ # @param [Bixby::JsonRequest] json_req
20
+ # @param [Bixby::APIChannel] api
21
+ def connect(json_req, api)
22
+ # no-op
23
+ end
24
+
25
+ # Channel disconnection event
26
+ # NOTE: only used by WebSocket channels and generally only implemented by
27
+ # the server-side.
28
+ #
29
+ # @param [Bixby::APIChannel] api
30
+ def disconnect(api)
31
+ # no-op
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,38 @@
1
+
2
+ module Bixby
3
+ class SignedJsonRequest < JsonRequest
4
+
5
+ attr_accessor :headers
6
+
7
+ def initialize(json_request, access_key=nil, secret_key=nil)
8
+ @operation = json_request.operation
9
+ @params = json_request.params
10
+ @access_key = access_key
11
+ @secret_key = secret_key
12
+ @headers = {}
13
+ end
14
+
15
+ # api-auth requires a path
16
+ def path
17
+ "/api"
18
+ end
19
+
20
+ def body=(str)
21
+ @body = str
22
+ end
23
+
24
+ def body
25
+ if @body.nil? then
26
+ hash = { :operation => operation, :params => params }
27
+ @body = MultiJson.dump(hash)
28
+ end
29
+ return @body
30
+ end
31
+
32
+ def to_wire
33
+ ApiAuth.sign!(self, @access_key, @secret_key)
34
+ body
35
+ end
36
+
37
+ end
38
+ end
@@ -14,7 +14,7 @@ class CommandSpec
14
14
 
15
15
  # Create new CommandSpec
16
16
  #
17
- # @params [Hash] params Hash of attributes to initialize with
17
+ # @param [Hash] params Hash of attributes to initialize with
18
18
  def initialize(params = nil)
19
19
  return if params.nil? or params.empty?
20
20
  params.each{ |k,v| self.send("#{k}=", v) if self.respond_to? "#{k}=" }
@@ -46,6 +46,27 @@ module Bixby
46
46
  opts[:filename] ||= Bixby.path("var", "bixby-agent.log")
47
47
  FileUtils.mkdir_p(File.dirname(opts[:filename]))
48
48
 
49
+ # configure stdout appender (used in debug modes, etc)
50
+ Logging.color_scheme( 'bright',
51
+ :levels => {
52
+ :info => :green,
53
+ :warn => :yellow,
54
+ :error => :red,
55
+ :fatal => [:white, :on_red]
56
+ },
57
+ :date => :blue,
58
+ :logger => :cyan,
59
+ :message => :magenta
60
+ )
61
+ Logging.appenders.stdout( 'stdout',
62
+ :auto_flushing => true,
63
+ :layout => Logging.layouts.pattern(
64
+ :pattern => pattern,
65
+ :color_scheme => 'bright'
66
+ )
67
+ )
68
+
69
+ # configure rolling file appender
49
70
  options = {
50
71
  :keep => 7,
51
72
  :roll_by => 'date',
@@ -54,7 +75,6 @@ module Bixby
54
75
  :auto_flushing => true,
55
76
  :layout => layout
56
77
  }.merge(opts)
57
-
58
78
  Logging.appenders.rolling_file("file", options)
59
79
 
60
80
  Logging::Logger.root.add_appenders("file")
@@ -0,0 +1,114 @@
1
+
2
+ require "bixby-common/websocket/async_response"
3
+ require "api-auth"
4
+
5
+ module Bixby
6
+ module WebSocket
7
+
8
+ # WebSocket API channel
9
+ #
10
+ # Implements a simple request/response interface over a WebSocket channel.
11
+ # Requests can be sent in either direction, in a sync or async manner.
12
+ class APIChannel < Bixby::APIChannel
13
+
14
+ attr_reader :ws
15
+
16
+ def initialize(ws, handler)
17
+ @ws = ws
18
+ @handler = handler
19
+ @responses = {}
20
+ @connected = false
21
+ end
22
+
23
+ # Perform RPC
24
+
25
+ # Execute the given request (synchronously)
26
+ #
27
+ # @param [JsonRequest] json_request
28
+ #
29
+ # @return [JsonResponse] response
30
+ def execute(json_request)
31
+ fetch_response( execute_async(json_request) )
32
+ end
33
+
34
+ # Execute the given request (asynchronously)
35
+ #
36
+ # @param [JsonRequest] json_request
37
+ #
38
+ # @return [String] request id
39
+ def execute_async(json_request)
40
+ request = Request.new(json_request)
41
+ id = request.id
42
+ @responses[id] = AsyncResponse.new(id)
43
+
44
+ EM.next_tick {
45
+ ws.send(request.to_wire)
46
+ }
47
+ id
48
+ end
49
+
50
+ # Fetch the response for the given request
51
+ #
52
+ # @param [String] request id
53
+ #
54
+ # @return [JsonResponse]
55
+ def fetch_response(id)
56
+ res = @responses[id].response
57
+ @responses.delete(id)
58
+ res
59
+ end
60
+
61
+
62
+ # Handle channel events
63
+
64
+ def connected?
65
+ @connected
66
+ end
67
+
68
+ # Open
69
+ def open(event)
70
+ # TODO extract Agent ID, if Agent
71
+ logger.debug "new channel opened"
72
+ @connected = true
73
+ end
74
+
75
+ # Close
76
+ #
77
+ # Can be fired either due to disconnection or failure to connect
78
+ def close(event)
79
+ if @connected then
80
+ logger.debug "client disconnected"
81
+ @connected = false
82
+ @handler.new(nil).disconnect(self)
83
+ end
84
+ end
85
+
86
+ # Message
87
+ #
88
+ # Fired whenever a message is received on the channel
89
+ def message(event)
90
+ logger.debug "got a message:\n#{event.data}"
91
+ req = Message.from_wire(event.data)
92
+
93
+ if req.type == "rpc" then
94
+ # Execute the requested method and return the result
95
+ json_response = @handler.new(req).handle(req.json_request)
96
+
97
+ # result = { :type => "rpc_result", :id => req.id, :data => json_response }
98
+ # ws.send(MultiJson.dump(result))
99
+ ws.send(Response.new(json_response, req.id).to_wire)
100
+
101
+ elsif req.type == "rpc_result" then
102
+ # Pass the result back to the caller
103
+ @responses[req.id].response = JsonResponse.from_json(req.body)
104
+
105
+ elsif req.type == "connect" then
106
+ @handler.new(req).connect(req.json_request, self)
107
+
108
+ end
109
+ end
110
+
111
+ end
112
+
113
+ end
114
+ end
@@ -0,0 +1,50 @@
1
+
2
+ require 'thread'
3
+
4
+ module Bixby
5
+ module WebSocket
6
+
7
+ # Asynchronously receive a response via WebSocket channel
8
+ class AsyncResponse
9
+
10
+ attr_reader :id
11
+
12
+ def initialize(id)
13
+ @id = id
14
+ @mutex = Mutex.new
15
+ @cond = ConditionVariable.new
16
+ @response = nil
17
+ @completed = false
18
+ end
19
+
20
+ # Set the response and signal any blocking threads
21
+ #
22
+ # @param [Object] obj result of request, usually a JsonResponse
23
+ def response=(obj)
24
+ @mutex.synchronize {
25
+ @completed = true
26
+ @response = obj
27
+ @cond.signal
28
+ }
29
+ end
30
+
31
+ # Has the request completed?
32
+ #
33
+ # @return [Boolean] true if completed
34
+ def completed?
35
+ @completed
36
+ end
37
+
38
+ # Retrieve the response, blocking until it is available
39
+ #
40
+ # @return [Object] response data
41
+ def response
42
+ return @response if @completed
43
+ @mutex.synchronize { @cond.wait(@mutex) }
44
+ return @response
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+ end