tuppari 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .DS_Store
6
+ .idea
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem 'rest-client', '1.6.7'
4
+ gem 'json', '1.7.4'
5
+
6
+ gem 'debugger'
7
+ gem 'rspec'
8
+
data/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # Tuppari client for Ruby
2
+
3
+ ## About Tuppari
4
+
5
+ [Tuppari](http://hakobera.github.com/tuppari/) is an experimental implementation of unbreakable message broadcasting system using WebSocket and Amazon Web Services by [@hakobera](https://github.com/hakobera).
6
+
7
+ https://github.com/hakobera/tuppari
8
+
9
+ https://github.com/hakobera/tuppari-servers
10
+
11
+
12
+ ## Tuppari for Ruby developers
13
+
14
+ You can easily build a Tuppari application by just using Ruby API. Let's get started now!
15
+
16
+ ### Gemfile
17
+
18
+ Prepare Gemfile as follows.
19
+
20
+ ```ruby
21
+ source 'https://rubygems.org'
22
+
23
+ gem 'tuppari'
24
+ ```
25
+
26
+ ### Getting Started
27
+
28
+ And then invoke `bundle console` to try Tuppari.
29
+
30
+ ```ruby
31
+ require 'tuppari'
32
+
33
+ Tuppari.create_account(YOUR_ID, PASSWORD)
34
+
35
+ tuppari = Tuppari.login(YOUR_ID, PASSWORD)
36
+
37
+ application = tuppari.create_application('first-tuppari')
38
+
39
+ open('tuppari.html', 'w') do |f|
40
+ html = <<"EOF"
41
+ <html>
42
+ <head>
43
+ <script src="http://cdn.tuppari.com/0.1.0/tuppari.min.js"></script>
44
+ <script>
45
+ var client, channel;
46
+ client = tuppari.createClient({applicationId: '#{application.id}'});
47
+ channel = client.subscribe('ch');
48
+ channel.bind('update', function (msg) {
49
+ console.log(msg);
50
+ });
51
+ </script>
52
+ </head>
53
+ <body>Check JavaScript console.</body>
54
+ </html>
55
+ EOF
56
+ f.write(html)
57
+ end
58
+
59
+ #
60
+ # [notice] Please open this 'tuppari.html' file in browser.
61
+ #
62
+
63
+ tuppari.application = application # or tuppari.load_application_by_name('first-tuppari')
64
+
65
+ tuppari.publish_message(
66
+ :channel => 'ch',
67
+ :event => 'update',
68
+ :message => "Hello, Tuppari! (#{Time.now})"
69
+ )
70
+ ```
71
+
72
+ ### Publishing messages without account authentication
73
+
74
+ Tuppari account authentication is not needed for publishing messages. And following values for the target application are required.
75
+
76
+ - application_id
77
+ - access_key_id (for the application)
78
+ - access_secret_key (for the application)
79
+
80
+ ```ruby
81
+ application = Tuppari::Application.new(
82
+ :id => 'xxxx',
83
+ :access_key_id => 'yyy',
84
+ :access_secret_key => 'zzz'
85
+ ) # or application = tuppari.get_application('first-tuppari')
86
+
87
+ Tuppari.publish_message(
88
+ :application => application,
89
+ :channel => 'ch',
90
+ :event => 'update',
91
+ :message => "Hello, Tuppari! (#{Time.now})"
92
+ )
93
+ ```
94
+
data/lib/tuppari.rb ADDED
@@ -0,0 +1,41 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'logger'
4
+ require 'json'
5
+ require 'rest_client'
6
+ require 'time'
7
+ require 'tuppari/auth'
8
+ require 'tuppari/application'
9
+ require 'tuppari/authentication_error'
10
+ require 'tuppari/client'
11
+ require 'tuppari/client_error'
12
+ require 'tuppari/server_url'
13
+
14
+ # Tuppari API client
15
+ #
16
+ # `Tuppari` is an experimental implementation of unbreakable message broadcasting system
17
+ # using WebSocket and Amazon Web Services.
18
+ #
19
+ # see also https://github.com/hakobera/tuppari-servers/wiki/API-Reference
20
+ #
21
+ module Tuppari
22
+
23
+ # Creates a new Tuppari::Client instance.
24
+ def self.new(params = {})
25
+ Tuppari::Client.new(params)
26
+ end
27
+
28
+ def self.create_account(account_name, password)
29
+ new().create_account(account_name, password)
30
+ end
31
+
32
+ def self.login(account_name, password)
33
+ new().login(account_name, password)
34
+ end
35
+
36
+ def self.publish_message(params)
37
+ Tuppari.new().publish_message(params)
38
+ end
39
+
40
+ end
41
+
@@ -0,0 +1,16 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Tuppari
4
+ class Application
5
+
6
+ attr_accessor :name, :id, :access_key_id, :access_secret_key
7
+
8
+ def initialize(params)
9
+ @name = params[:name]
10
+ @id = params[:id]
11
+ @access_key_id = params[:access_key_id]
12
+ @access_secret_key = params[:access_secret_key]
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,107 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'openssl'
4
+ require 'digest'
5
+ require 'json'
6
+
7
+ module Tuppari
8
+ module Auth
9
+
10
+ def self.create_canonical_uri(uri)
11
+ if %r{^http(s)*://}.match(uri)
12
+ uri.sub(%r{^http(s)*://[^/]+/}, '/')
13
+ elsif %r{^[^/]}.match(uri)
14
+ "/#{uri}"
15
+ else
16
+ uri
17
+ end
18
+ end
19
+
20
+ def self.create_canonical_query_string(query_string)
21
+ if query_string.nil?
22
+ ''
23
+ else
24
+ query_string.split("&").sort_by { |e| e.split("=").first }.join('&')
25
+ end
26
+ end
27
+
28
+ def self.create_canonical_headers(headers)
29
+ if headers.nil?
30
+ ''
31
+ else
32
+ downcase_keys(headers).to_a.sort_by { |k, v| k }.map { |k, v| "#{k}:#{v}" }.join("\n")
33
+ end
34
+ end
35
+
36
+ def self.create_signed_headers(headers)
37
+ if headers.nil?
38
+ ''
39
+ else
40
+ downcase_keys(headers).to_a.sort_by { |k, v| k }.map { |k, v| k }.join(';')
41
+ end
42
+ end
43
+
44
+ def self.create_body_hash(body_params)
45
+ sha256 = Digest::SHA256.new
46
+ digest = sha256.digest(JSON.unparse(body_params))
47
+ Digest.hexencode(digest)
48
+ end
49
+
50
+ def self.create_canonical_request(method, uri, query_string, headers, body_params)
51
+ request = []
52
+ request << method
53
+ request << create_canonical_uri(uri)
54
+ request << create_canonical_query_string(query_string)
55
+ request << create_canonical_headers(headers)
56
+ request << create_signed_headers(headers)
57
+ request << create_body_hash(body_params)
58
+ request.join("\n")
59
+ end
60
+
61
+ def self.create_string_to_sign(canonical_request, request_time)
62
+ result = []
63
+ result << "SHA256"
64
+ result << request_time.gmtime.strftime("%Y%m%dT%H%M%SZ")
65
+ sha256 = Digest::SHA256.new
66
+ digest = sha256.digest(canonical_request)
67
+ result << Digest.hexencode(digest)
68
+ result.join("\n")
69
+ end
70
+
71
+ def self.create_signature(secret, string_to_sign, request_time, host)
72
+ signing_key = create_hmac_hash_value(create_hmac_hash_value("TUPPARI#{secret}", request_time.gmtime.strftime("%Y%m%dT%H%M%SZ")), host)
73
+ create_hmac_hash_value(signing_key, string_to_sign)
74
+ end
75
+
76
+ def self.create_authorization_header(params)
77
+ id = params[:id]
78
+ secret = params[:secret]
79
+ method = params[:method] || 'POST'
80
+ uri = params[:uri]
81
+ host = params[:host] || 'api.tuppari.com'
82
+ query_string = params[:query_string] || ''
83
+ headers = params[:headers] || {}
84
+ body_params = params[:body_params] || {}
85
+ request_time = params[:request_time] || Time.now
86
+
87
+ signed_headers = create_signed_headers(headers)
88
+ canonical_request = create_canonical_request(method, uri, query_string, headers, body_params)
89
+ string_to_sign = create_string_to_sign(canonical_request, request_time)
90
+ signature = create_signature(secret, string_to_sign, request_time, host)
91
+ "HMAC-SHA256 Credential=#{id},SignedHeaders=#{signed_headers},Signature=#{signature}"
92
+ end
93
+
94
+ protected
95
+
96
+ def self.downcase_keys(headers)
97
+ headers.each_with_object({}) { |(k, v), hash|
98
+ hash[k.downcase] = v
99
+ }
100
+ end
101
+
102
+ def self.create_hmac_hash_value(key, data)
103
+ OpenSSL::HMAC.hexdigest('sha256', key, data)
104
+ end
105
+
106
+ end
107
+ end
@@ -0,0 +1,6 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Tuppari
4
+ class AuthenticationError < Exception
5
+ end
6
+ end
@@ -0,0 +1,290 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'logger'
4
+ require 'time'
5
+ require 'json'
6
+ require 'rest_client'
7
+
8
+ require 'tuppari'
9
+
10
+ module Tuppari
11
+ class Client
12
+
13
+ attr_accessor :account_name, :account_secret, :application, :log
14
+
15
+ def initialize(params = {})
16
+ @log = Logger.new(params[:log_output] || STDOUT)
17
+ @log.level = params[:log_level] || Logger::INFO
18
+ @account_name = params[:account_name]
19
+ @account_secret = params[:account_secret]
20
+ setup_application(params)
21
+ end
22
+
23
+ def load_application_by_name(application_name)
24
+ setup_application({:application_name => application_name})
25
+ self
26
+ end
27
+
28
+ def create_account(account_name, password)
29
+ body = JSON.unparse({"accountName" => account_name, "password" => password})
30
+ begin
31
+ RestClient.post(Tuppari::ServerURL::ACCOUNTS_REGISTER, body, :content_type => :json)
32
+ rescue RestClient::Exception => e
33
+ @log.warn("Failed to create a new Tuppari account (#{e}, #{e.http_body})")
34
+ raise e
35
+ rescue Exception => e
36
+ @log.warn("Failed to create a new Tuppari account (#{e})")
37
+ raise e
38
+ end
39
+ end
40
+
41
+ def login(account_name, password)
42
+ body = JSON.unparse({:accountName => account_name, :password => password})
43
+ begin
44
+ body = RestClient.post(Tuppari::ServerURL::ACCOUNTS_AUTH, body, :content_type => :json)
45
+ credentials = JSON.parse(body)['credentials']
46
+ @account_name = credentials['id']
47
+ @account_secret = credentials['secret']
48
+ @log.debug {
49
+ "Authenticated tuppari user (account_name:#{@account_name}, account_secret:#{@account_secret})"
50
+ }
51
+ rescue RestClient::Exception => e
52
+ @log.warn("Failed to authenticate your Tuppari account (#{e}, #{e.http_body})")
53
+ raise e
54
+ rescue Exception => e
55
+ @log.warn("Failed to authenticate your Tuppari account (#{e})")
56
+ raise e
57
+ end
58
+ self
59
+ end
60
+
61
+ def is_authenticated?
62
+ !@account_name.nil? && !@account_secret.nil?
63
+ end
64
+
65
+ def create_application(application_name)
66
+ unless is_authenticated?
67
+ raise Tuppari::AuthenticationError.new("Call #authenticate(account_name, password) before.")
68
+ end
69
+
70
+ time = Time.now
71
+ headers = headers('CreateApplication', time)
72
+ body_params = {:applicationName => application_name.to_s}
73
+ auth_header = Tuppari::Auth.create_authorization_header(
74
+ :id => @account_name,
75
+ :secret => @account_secret,
76
+ :method => 'POST',
77
+ :uri => Tuppari::ServerURL::APPLICATIONS,
78
+ :host => 'api.tuppari.com',
79
+ :query_string => '',
80
+ :headers => headers,
81
+ :body_params => body_params,
82
+ :request_time => time
83
+ )
84
+ begin
85
+ body = RestClient.post(Tuppari::ServerURL::APPLICATIONS,
86
+ JSON.unparse(body_params),
87
+ headers.merge(:authorization => auth_header))
88
+ @log.debug {
89
+ "create_application response: #{body}"
90
+ }
91
+ @log.info {
92
+ "Tuppari application is created. (#{application_name})"
93
+ }
94
+ response = JSON.parse(body)
95
+ Tuppari::Application.new(
96
+ :name => response['applicationName'],
97
+ :id => response['applicationId'],
98
+ :access_key_id => response['accessKeyId'],
99
+ :access_secret_key => response['accessSecretKey']
100
+ )
101
+ rescue RestClient::Exception => e
102
+ @log.warn("Failed to create a new Tuppari application (#{e}, #{e.http_body})")
103
+ raise e
104
+ rescue Exception => e
105
+ @log.warn("Failed to create a new Tuppari application (#{e})")
106
+ raise e
107
+ end
108
+ end
109
+
110
+ def delete_application(application_name)
111
+ unless is_authenticated?
112
+ raise Tuppari::AuthenticationError.new("Call #authenticate(account_name, password) before.")
113
+ end
114
+
115
+ time = Time.now
116
+ headers = headers('DeleteApplication', time)
117
+ body_params = {:applicationName => application_name}
118
+ auth_header = Tuppari::Auth.create_authorization_header(
119
+ :id => @account_name,
120
+ :secret => @account_secret,
121
+ :method => 'POST',
122
+ :uri => Tuppari::ServerURL::APPLICATIONS,
123
+ :host => 'api.tuppari.com',
124
+ :query_string => '',
125
+ :headers => headers,
126
+ :body_params => body_params,
127
+ :request_time => time
128
+ )
129
+ begin
130
+ RestClient.post(Tuppari::ServerURL::APPLICATIONS,
131
+ JSON.unparse(body_params),
132
+ headers.merge(:authorization => auth_header))
133
+ @log.info {
134
+ "Tuppari application is deleted. (#{application_name})"
135
+ }
136
+ rescue RestClient::Exception => e
137
+ @log.warn("Failed to delete a Tuppari application (#{e}, #{e.http_body})")
138
+ raise e
139
+ rescue Exception => e
140
+ @log.warn("Failed to delete a Tuppari application (#{e})")
141
+ raise e
142
+ end
143
+ nil
144
+ end
145
+
146
+ def get_application(application_name)
147
+ get_application_list().find { |app| app.name == application_name }
148
+ end
149
+
150
+ def get_application_list()
151
+ unless is_authenticated?
152
+ raise Tuppari::AuthenticationError.new("Call #authenticate(account_name, password) before.")
153
+ end
154
+
155
+ time = Time.now
156
+ headers = headers('ListApplication', time)
157
+ body_params = {}
158
+ auth_header = Tuppari::Auth.create_authorization_header(
159
+ :id => @account_name,
160
+ :secret => @account_secret,
161
+ :method => 'POST',
162
+ :uri => Tuppari::ServerURL::APPLICATIONS,
163
+ :host => 'api.tuppari.com',
164
+ :query_string => '',
165
+ :headers => headers,
166
+ :body_params => body_params,
167
+ :request_time => time
168
+ )
169
+ begin
170
+ body = RestClient.post(Tuppari::ServerURL::APPLICATIONS,
171
+ JSON.unparse(body_params),
172
+ headers.merge(:authorization => auth_header))
173
+ @log.debug {
174
+ "get_application_list response: #{response}"
175
+ }
176
+ JSON.parse(body).to_a.map { |name, app|
177
+ Tuppari::Application.new(
178
+ :name => app['name'],
179
+ :id => app['applicationId'],
180
+ :access_key_id => app['accessKeyId'],
181
+ :access_secret_key => app['accessSecretKey']
182
+ )
183
+ }
184
+ rescue RestClient::Exception => e
185
+ @log.warn("Failed to get the Tuppari application list (#{e}, #{e.http_body})")
186
+ raise e
187
+ rescue Exception => e
188
+ @log.warn("Failed to get the Tuppari application list (#{e})")
189
+ raise e
190
+ end
191
+ end
192
+
193
+ def publish_message(params)
194
+
195
+ setup_application(params)
196
+ if @application.nil?
197
+ raise Tuppari::ClientError.new("Specify Tuppari application to publish messages.")
198
+ end
199
+
200
+ channel = params[:channel]
201
+ event = params[:event]
202
+ message = params[:message]
203
+
204
+ time = Time.now
205
+ headers = headers('PublishMessage', time)
206
+ body_params = {
207
+ :applicationId => @application.id,
208
+ :channel => channel,
209
+ :event => event,
210
+ :message => message
211
+ }
212
+ auth_header = Tuppari::Auth.create_authorization_header(
213
+ :id => @application.access_key_id,
214
+ :secret => @application.access_secret_key,
215
+ :method => 'POST',
216
+ :uri => Tuppari::ServerURL::MESSAGES,
217
+ :host => 'api.tuppari.com',
218
+ :query_string => '',
219
+ :headers => headers,
220
+ :body_params => body_params,
221
+ :request_time => time
222
+ )
223
+ begin
224
+ body = RestClient.post(Tuppari::ServerURL::MESSAGES,
225
+ JSON.unparse(body_params),
226
+ headers.merge(:authorization => auth_header))
227
+ @log.debug {
228
+ "Tuppari message has been sent. (#{@application.id},#{channel},#{event},#{message})"
229
+ }
230
+ JSON.parse(body)
231
+ rescue RestClient::Exception => e
232
+ @log.warn {
233
+ "Failed to publish a Tuppari message (#{e}, #{e.http_body}, application_id:#{@application.id}, " +
234
+ "channel:#{channel}, event:#{event}, message:#{message})"
235
+ }
236
+ raise e
237
+ rescue Exception => e
238
+ @log.warn {
239
+ "Failed to publish a Tuppari message (#{e}, application_id:#{@application.id}, " +
240
+ "channel:#{channel}, event:#{event}, message:#{message})"
241
+ }
242
+ raise e
243
+ end
244
+ end
245
+
246
+ def get_service_info()
247
+ begin
248
+ body = RestClient.get(Tuppari::ServerURL::INFO, 'Content-type' => 'application/json')
249
+ JSON.parse(body)
250
+ rescue RestClient::Exception => e
251
+ @log.warn("Failed to get the Tuppari service info (#{e}, #{e.http_body})")
252
+ raise e
253
+ rescue Exception => e
254
+ @log.warn("Failed to get the Tuppari service info (#{e})")
255
+ raise e
256
+ end
257
+ end
258
+
259
+ protected
260
+
261
+ def setup_application(params)
262
+ if !params[:application].nil?
263
+ app = params[:application]
264
+ if app.is_a? Tuppari::Application
265
+ @application = app
266
+ else
267
+ raise Tuppari::ClientError.new("application should be a Tuppari::Application object.")
268
+ end
269
+ elsif !params[:application_name].nil?
270
+ application_name = params[:application_name]
271
+ application = get_application_list().find { |app| app.name == application_name }
272
+ if application.nil?
273
+ raise Tuppari::ClientError.new("Application is not found (application_name: #{application_name})")
274
+ else
275
+ @application = application
276
+ end
277
+ end
278
+ end
279
+
280
+ def headers(operation, time)
281
+ {
282
+ 'Host' => 'api.tuppari.com',
283
+ 'Content-type' => 'application/json',
284
+ 'X-Tuppari-Date' => time.gmtime.rfc2822,
285
+ 'X-Tuppari-Operation' => operation
286
+ }
287
+ end
288
+
289
+ end
290
+ end
@@ -0,0 +1,6 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Tuppari
4
+ class ClientError < Exception
5
+ end
6
+ end
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Tuppari
4
+ module ServerURL
5
+
6
+ BASE_URL = 'https://api.tuppari.com'
7
+
8
+ ACCOUNTS_REGISTER = BASE_URL + '/accounts/register'
9
+
10
+ ACCOUNTS_AUTH = BASE_URL + '/accounts/auth'
11
+
12
+ APPLICATIONS = BASE_URL + '/applications'
13
+
14
+ MESSAGES = BASE_URL + '/messages'
15
+
16
+ INFO = BASE_URL + '/info'
17
+
18
+ end
19
+ end
@@ -0,0 +1,6 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Tuppari
4
+ VERSION = "0.1.1"
5
+ end
6
+
data/spec/README.md ADDED
@@ -0,0 +1,6 @@
1
+ Before testing, update your `~/.bash_profile` as follows:
2
+
3
+ ```sh
4
+ export TUPPARI_ACCOUNT_NAME=YOUR_ACCOUNT
5
+ export TUPPARI_PASSWORD=YOUR_PASSWORd
6
+ ```
@@ -0,0 +1,128 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'tuppari'
4
+
5
+ describe Tuppari::Auth do
6
+
7
+ it "should have correct #create_canonical_uri" do
8
+ Tuppari::Auth.create_canonical_uri('foo/bar').should eq('/foo/bar')
9
+ Tuppari::Auth.create_canonical_uri('/foo/bar').should eq('/foo/bar')
10
+ Tuppari::Auth.create_canonical_uri('http://www.google.com/foo/bar').should eq('/foo/bar')
11
+ end
12
+
13
+ it "should have correct #create_canonical_query_string" do
14
+ Tuppari::Auth.create_canonical_query_string(nil).should eq('')
15
+ Tuppari::Auth.create_canonical_query_string('').should eq('')
16
+ Tuppari::Auth.create_canonical_query_string('a=b').should eq('a=b')
17
+ Tuppari::Auth.create_canonical_query_string('a=b&c=d').should eq('a=b&c=d')
18
+ Tuppari::Auth.create_canonical_query_string('c=d&a=b').should eq('a=b&c=d')
19
+ Tuppari::Auth.create_canonical_query_string('c=d&a=b&c=e').should eq('a=b&c=d&c=e')
20
+ end
21
+
22
+ it "should have correct #create_canonical_headers" do
23
+ Tuppari::Auth.create_canonical_headers(nil).should eq('')
24
+ Tuppari::Auth.create_canonical_headers({}).should eq('')
25
+
26
+ headers = {
27
+ 'X-Tuppari-Operation' => 'CreateApplication',
28
+ 'Content-type' => 'application/json',
29
+ 'Host' => 'api.tuppari.com'
30
+ }
31
+ Tuppari::Auth.create_canonical_headers(headers).should eq("content-type:application/json\nhost:api.tuppari.com\nx-tuppari-operation:CreateApplication")
32
+ end
33
+
34
+ it "should have correct #create_signed_headers" do
35
+ Tuppari::Auth.create_signed_headers(nil).should eq('')
36
+ Tuppari::Auth.create_signed_headers({}).should eq('')
37
+
38
+ headers = {
39
+ 'X-Tuppari-Operation' => 'CreateApplication',
40
+ 'Content-type' => 'application/json',
41
+ 'Host' => 'api.tuppari.com'
42
+ }
43
+ Tuppari::Auth.create_signed_headers(headers).should eq('content-type;host;x-tuppari-operation')
44
+ end
45
+
46
+ it 'should have correct #create_body_hash' do
47
+ Tuppari::Auth.create_body_hash({'a' => 'b', 'c' => 'd'}).should eq('b85c7da93e8790518898c280e15e3f1af5d46bf4aaa4407690f0f0a3b0316478')
48
+ end
49
+
50
+ it 'should have correct #create_canonical_request' do
51
+ headers = {
52
+ 'X-Tuppari-Operation' => 'CreateApplication',
53
+ 'Content-type' => 'application/json',
54
+ 'Host' => 'api.tuppari.com'
55
+ }
56
+ body_params = {'applicationName' => 'example1'}
57
+ request = Tuppari::Auth.create_canonical_request('POST', '/test', 'b=v2&a=v1', headers, body_params)
58
+
59
+ request.should eq("POST\n" +
60
+ "/test\n" +
61
+ "a=v1&b=v2\n" +
62
+ "content-type:application/json\n" +
63
+ "host:api.tuppari.com\n" +
64
+ "x-tuppari-operation:CreateApplication\n" +
65
+ "content-type;host;x-tuppari-operation\n" +
66
+ "8f2d5fe4a93000d3546e578d265fc936806f6ef6dc6f7ee87715e1a5c514c168")
67
+ end
68
+
69
+ it 'should have correct #create_string_to_sign' do
70
+ request = "POST\n" +
71
+ "/test\n" +
72
+ "a=v1&b=v2\n" +
73
+ "content-type:application/json\n" +
74
+ "host:api.tuppari.com\n" +
75
+ "x-tuppari-operation:CreateApplication\n" +
76
+ "content-type;host;x-tuppari-operation\n" +
77
+ "8f2d5fe4a93000d3546e578d265fc936806f6ef6dc6f7ee87715e1a5c514c168"
78
+ Tuppari::Auth.create_string_to_sign(request, Time.new(2011, 2, 3, 4, 5, 6)).should(
79
+ eq("SHA256\n" +"20110202T190506Z\n" +"152176000cc08c7d9d0558bc3a50368aa38619a695ad20f50bec1344429cb315"))
80
+
81
+ end
82
+
83
+ it 'should have correct #create_signature' do
84
+ secret = 'secretKey1'
85
+ string_to_sign = "SHA256\n" +
86
+ "19700101T000000Z\n" +
87
+ "152176000cc08c7d9d0558bc3a50368aa38619a695ad20f50bec1344429cb315"
88
+ time = Time.new(1970, 1, 1, 0, 0, 0, "+00:00")
89
+ host = 'api.tuppari.com'
90
+ expected_signature = '4815ff1681a278e7c852902ea3604f17831a80a78dc0ff82f5142598a034509b'
91
+ (1...1000).each do |i|
92
+ signature = Tuppari::Auth.create_signature(secret, string_to_sign, time, host)
93
+ signature.should eq(expected_signature)
94
+ end
95
+
96
+ secret2 = 'secretKey2'
97
+ signature1 = Tuppari::Auth.create_signature(secret, string_to_sign, time, host)
98
+ signature2 = Tuppari::Auth.create_signature(secret2, string_to_sign, time, host)
99
+ signature2.should_not eq(signature1)
100
+ end
101
+
102
+ it 'should have correct #create_authorization_header' do
103
+ header = Tuppari::Auth.create_authorization_header(
104
+ :id => 'accessKeyId',
105
+ :secret => 'accessSecretKey',
106
+ :method => 'POST',
107
+ :host => 'api.tuppari.com',
108
+ :uri => '/test',
109
+ :query_string => 'a=v1&b=v2',
110
+ :headers => {
111
+ 'Host' => 'api.tuppari.com',
112
+ 'Content-Type' => 'application/json',
113
+ 'X-Tuppari-Operation' => 'CreateApplication'
114
+ },
115
+ :body_params => {
116
+ 'applicationName' => 'example1'
117
+ },
118
+ :request_time => Time.new(1970, 1, 1, 0, 0, 0, "+00:00")
119
+ )
120
+ header.should eq("HMAC-SHA256 Credential=accessKeyId,SignedHeaders=content-type;host;x-tuppari-operation,Signature=050f8711271747d4f63a3caa3ffb420e4cd5a0e9d9dda8ba7e4faad6794c40d0")
121
+ end
122
+
123
+ it 'should have correct #create_hmac_hash_value' do
124
+ result = Tuppari::Auth.create_hmac_hash_value('123', 'abc')
125
+ result.should eq('8f16771f9f8851b26f4d460fa17de93e2711c7e51337cb8a608a0f81e1c1b6ae')
126
+ end
127
+
128
+ end
@@ -0,0 +1,111 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'tuppari'
4
+
5
+ require 'logger'
6
+ require 'ruby-debug'
7
+
8
+ describe Tuppari do
9
+
10
+ account_name = ENV['TUPPARI_ACCOUNT_NAME']
11
+ password = ENV['TUPPARI_PASSWORD']
12
+
13
+ =begin
14
+
15
+ it "should create a new account" do
16
+ tuppari = Tuppari.new()
17
+ response = tuppari.create_account("account_name", "password")
18
+ puts response
19
+ end
20
+
21
+ it "should create a new account" do
22
+ tuppari = Tuppari.new(:account_name => account_name, :account_secret => account_secret)
23
+ response = tuppari.delete_application("application_name")
24
+ puts response
25
+ end
26
+
27
+ =end
28
+
29
+ it "should be created with account_name/account_secret" do
30
+ tuppari = Tuppari.new(
31
+ :account_name => account_name,
32
+ :account_secret => Tuppari.login(account_name, password).account_secret
33
+ )
34
+ tuppari.is_authenticated?.should eq(true)
35
+ end
36
+
37
+ it "should login" do
38
+ tuppari = Tuppari.new()
39
+ tuppari.is_authenticated?.should eq(false)
40
+ tuppari.login(account_name, password)
41
+ tuppari.is_authenticated?.should eq(true)
42
+ end
43
+
44
+ it "should raise Tuppari::AuthenticationError when creating a new applications without authentication" do
45
+ tuppari = Tuppari.new()
46
+ name = Time.now.tv_sec.to_s
47
+ begin
48
+ tuppari.create_application(name)
49
+ fail()
50
+ rescue Tuppari::AuthenticationError
51
+ end
52
+ end
53
+
54
+ it "should create a new application" do
55
+ tuppari = Tuppari.new(:log_level => Logger::WARN).login(account_name, password)
56
+ name = Time.now.tv_sec.to_s
57
+ app = tuppari.create_application(name)
58
+ app.name.should eq(name)
59
+ app.id.nil?.should eq(false)
60
+ app.access_key_id.nil?.should eq(false)
61
+ app.access_secret_key.nil?.should eq(false)
62
+ end
63
+
64
+ it "should raise Tuppari::AuthenticationError when getting my application list without authentication" do
65
+ tuppari = Tuppari.new()
66
+ begin
67
+ tuppari.get_application_list()
68
+ fail()
69
+ rescue Tuppari::AuthenticationError
70
+ end
71
+ end
72
+
73
+ it "should get my application list" do
74
+ tuppari = Tuppari.new(:log_level => Logger::WARN).login(account_name, password)
75
+ response = tuppari.get_application_list()
76
+ response.size.should > 0
77
+ end
78
+
79
+ it "should raise Tuppari::AuthenticationError when publishing messages without authentication" do
80
+ tuppari = Tuppari.new()
81
+ begin
82
+ response = tuppari.publish_message(
83
+ :application_name => 'tuppari-sample',
84
+ :channel => 'channel_name',
85
+ :event => 'event_name',
86
+ :message => 'Hello, Tuppari!'
87
+ )
88
+ fail()
89
+ rescue Tuppari::AuthenticationError
90
+ end
91
+ end
92
+
93
+
94
+ it "should publish messages" do
95
+ tuppari = Tuppari.new(:log_level => Logger::WARN).login(account_name, password)
96
+ response = tuppari.publish_message(
97
+ :application_name => 'tuppari-sample',
98
+ :channel => 'channel_name',
99
+ :event => 'event_name',
100
+ :message => 'Hello, Tuppari!'
101
+ )
102
+ response.size.should > 0
103
+ end
104
+
105
+ it "should get the service info" do
106
+ info = Tuppari.new().get_service_info()
107
+ info['name'].should eq("gyoji")
108
+ info['version'].nil?.should eq(false)
109
+ end
110
+
111
+ end
data/tuppari.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ $:.push File.expand_path("../lib", __FILE__)
4
+ require "tuppari/version"
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "tuppari"
8
+ s.version = Tuppari::VERSION
9
+ s.authors = ["Kazuhiro Sera"]
10
+ s.email = ["seratch@gmail.com"]
11
+ s.homepage = "https://github.com/tuppari/tuppari-ruby"
12
+ s.summary = %q{Tuppari client for Ruby}
13
+ s.description = %q{Tuppari is an experimental implementation of unbreakable message broadcasting system using WebSocket and Amazon Web Services by @hakobera.}
14
+
15
+ s.rubyforge_project = "tuppari"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.required_ruby_version = '>= 1.9.0'
23
+
24
+ s.add_dependency('rest-client', '1.6.7')
25
+ s.add_dependency('json', '1.7.4')
26
+
27
+ end
28
+
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tuppari
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kazuhiro Sera
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rest-client
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - '='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.6.7
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - '='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.6.7
30
+ - !ruby/object:Gem::Dependency
31
+ name: json
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - '='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.7.4
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - '='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.7.4
46
+ description: Tuppari is an experimental implementation of unbreakable message broadcasting
47
+ system using WebSocket and Amazon Web Services by @hakobera.
48
+ email:
49
+ - seratch@gmail.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - Gemfile
56
+ - README.md
57
+ - lib/tuppari.rb
58
+ - lib/tuppari/application.rb
59
+ - lib/tuppari/auth.rb
60
+ - lib/tuppari/authentication_error.rb
61
+ - lib/tuppari/client.rb
62
+ - lib/tuppari/client_error.rb
63
+ - lib/tuppari/server_url.rb
64
+ - lib/tuppari/version.rb
65
+ - spec/README.md
66
+ - spec/tuppari/auth_spec.rb
67
+ - spec/tuppari_spec.rb
68
+ - tuppari.gemspec
69
+ homepage: https://github.com/tuppari/tuppari-ruby
70
+ licenses: []
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: 1.9.0
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project: tuppari
89
+ rubygems_version: 1.8.24
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: Tuppari client for Ruby
93
+ test_files:
94
+ - spec/README.md
95
+ - spec/tuppari/auth_spec.rb
96
+ - spec/tuppari_spec.rb