bixby-common 0.3.16 → 0.4.0

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.
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