amaze_sns 1.3.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -2,26 +2,31 @@ Amazon SNS gem
2
2
  ==============
3
3
 
4
4
  Introduction
5
- ---------
5
+ ------------
6
6
 
7
7
  A Ruby gem for use with the Amazon Simple Notification service (http://aws.amazon.com/sns/).
8
8
 
9
9
  Usage
10
- ---------------
10
+ ------
11
+
12
+ ### Quickstart Guide ###
11
13
 
12
- gem install amaze_sns
14
+ gem install amaze_sns
13
15
 
14
- require 'amaze-sns'
16
+ require 'amaze-sns'
15
17
 
16
- AmazeSNS.skey = <your amazon aws secret key>
18
+ AmazeSNS.skey = 'your amazon aws secret key'
17
19
 
18
- AmazeSNS.akey = <your amazon aws access key>
20
+ AmazeSNS.akey = 'your amazon aws access key'
21
+
22
+ # add new topic to local hash and to SNS
23
+ # You need to call create in order to access the ARN of the topic
24
+ AmazeSNS['your_topic_name'].create
25
+
26
+ AmazeSNS['your_topic_name'].delete # removes it from both SNS and local hash
19
27
 
20
- AmazeSNS['your_topic_name'] # creates a new Topic Object but not yet published
21
- AmazeSNS['your_topic_name'].create # add new topic to local hash and to SNS
22
- AmazeSNS['your_topic_name'].delete # removes it from both SNS and local hash
28
+ AmazeSNS.logger = my_logger # set a logger for the response`
23
29
 
24
- AmazeSNS.logger = my_logger # set a logger for the response
25
30
 
26
31
  Dependencies
27
32
  ---------------
@@ -34,14 +39,21 @@ higher than 0.2.10 although the gemspec has been updated to reflect this
34
39
 
35
40
 
36
41
  Tests
37
- ---------------
42
+ ------
38
43
  The specs are partly working at the moment as the gem is still under development
39
44
 
40
45
  The gem itself has been tested on systems running ruby 1.8.6, 1.8.7, 1.9.2
41
46
 
42
47
 
48
+ License
49
+ -------
50
+
51
+ (The MIT License)
52
+
53
+ Copyright © 2010 Chee Yeo, 29 Steps UK
54
+
55
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
43
56
 
44
- Copyright
45
- ---------
57
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
46
58
 
47
- Copyright (c) 2010 29 Steps UK. See LICENSE for details.
59
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/lib/amaze/request.rb CHANGED
@@ -1,5 +1,6 @@
1
-
1
+ require "rubygems"
2
2
  require 'crack/xml'
3
+ require 'ruby-debug'
3
4
 
4
5
  require File.dirname(__FILE__) + "/helpers"
5
6
  require File.dirname(__FILE__) + "/exceptions"
@@ -8,30 +9,12 @@ require 'em-http'
8
9
 
9
10
 
10
11
  class Request
12
+ include EM::Deferrable
11
13
 
12
- attr_accessor :params, :options, :httpresponse
14
+ attr_accessor :params, :httpresponse
13
15
 
14
- def initialize(params, options={})
16
+ def initialize(params)
15
17
  @params = params
16
- @options = options
17
- end
18
-
19
- def process2
20
- query_string = canonical_querystring(@params)
21
- string_to_sign = "GET
22
- #{AmazeSNS.host}
23
- /
24
- #{query_string}"
25
-
26
- hmac = HMAC::SHA256.new(AmazeSNS.skey)
27
- hmac.update( string_to_sign )
28
- signature = Base64.encode64(hmac.digest).chomp
29
-
30
- params['Signature'] = signature
31
- querystring2 = params.collect { |key, value| [url_encode(key), url_encode(value)].join("=") }.join('&') # order doesn't matter for the actual request
32
- response = HttpClient.get "#{AmazeSNS.host}?#{querystring2}"
33
- parsed_response = Crack::XML.parse(response)
34
- return parsed_response
35
18
  end
36
19
 
37
20
  def process
@@ -55,10 +38,13 @@ string_to_sign = "GET
55
38
 
56
39
  require 'em-http' unless defined?(EventMachine::HttpRequest)
57
40
 
58
- @httpresponse ||= http_class.new("http://#{AmazeSNS.host}/?#{querystring2}").send(:get)
59
- @httpresponse.callback{ success_callback }
60
- @httpresponse.errback{ error_callback }
61
- nil
41
+ @httpresponse ||= http_class.new("http://#{AmazeSNS.host}/?").get({
42
+ :query => querystring2, :timeout => 2
43
+ })
44
+
45
+ # a bit misleading but call is still successful even if the status code is not 200
46
+ @httpresponse.callback{ success_callback }
47
+ @httpresponse.errback{ error_callback }
62
48
  end
63
49
 
64
50
  def http_class
@@ -68,6 +54,8 @@ string_to_sign = "GET
68
54
 
69
55
  def success_callback
70
56
  case @httpresponse.response_header.status
57
+ when 200
58
+ self.succeed(@httpresponse)
71
59
  when 403
72
60
  raise AuthorizationError
73
61
  when 500
@@ -77,12 +65,14 @@ string_to_sign = "GET
77
65
  when 404
78
66
  raise NotFoundError
79
67
  else
80
- call_user_success_handler
68
+ self.fail("Call to Amazon SNS API failed")
81
69
  end #end case
82
70
  end
83
71
 
84
72
  def call_user_success_handler
85
- @options[:on_success].call(httpresponse) if options[:on_success].respond_to?(:call)
73
+ #puts "#{@options[:on_success]}"
74
+ @options[:on_success].call(@httpresponse) if @options[:on_success].respond_to?(:call)
75
+ #self.succeed(@httpresponse)
86
76
  end
87
77
 
88
78
  def error_callback
data/lib/amaze/topic.rb CHANGED
@@ -5,18 +5,26 @@ require "json"
5
5
 
6
6
  class Topic
7
7
 
8
- attr_accessor :topic, :arn, :attrs
8
+ attr_accessor :topic, :arn, :attributes
9
9
 
10
10
  def initialize(topic, arn='')
11
11
  @topic = topic
12
12
  @arn = arn
13
- @attrs ||= {}
13
+ @attributes = {}
14
14
  end
15
15
 
16
- def generate_request(params,&blk)
17
- req_options={}
18
- req_options[:on_success] = blk if blk
19
- Request.new(params, req_options).process
16
+ def generate_request(params)
17
+ request = Request.new(params)
18
+ request.process
19
+
20
+ request.callback do |data|
21
+ yield data
22
+ end
23
+
24
+ request.errback do |resp|
25
+ puts "ERROR - #{resp.inspect}"
26
+ EM.stop
27
+ end
20
28
  end
21
29
 
22
30
  # for running th EM loop w/o repetitions
@@ -80,7 +88,7 @@ class Topic
80
88
  def attrs
81
89
  outcome = nil
82
90
  params = {
83
- 'TopicArn' => "#{arn}",
91
+ 'TopicArn' => "#{@arn}",
84
92
  'Action' => 'GetTopicAttributes',
85
93
  'SignatureMethod' => 'HmacSHA256',
86
94
  'SignatureVersion' => 2,
@@ -93,6 +101,7 @@ class Topic
93
101
  parsed_response = Crack::XML.parse(response.response)
94
102
  res = parsed_response['GetTopicAttributesResponse']['GetTopicAttributesResult']['Attributes']["entry"]
95
103
  outcome = make_hash(res) #res["entry"] is an array of hashes - need to turn it into hash with key value
104
+ self.attributes = outcome
96
105
  EM.stop
97
106
  end
98
107
  }
@@ -108,6 +117,7 @@ class Topic
108
117
 
109
118
  def set_attrs(opts)
110
119
  outcome = nil
120
+ #TODO: check format of opts to make sure they are compilant with API
111
121
  params = {
112
122
  'AttributeName' => "#{opts[:name]}",
113
123
  'AttributeValue' => "#{opts[:value]}",
@@ -123,6 +133,8 @@ class Topic
123
133
  generate_request(params) do |response|
124
134
  parsed_response = Crack::XML.parse(response.response)
125
135
  outcome = parsed_response['SetTopicAttributesResponse']['ResponseMetadata']['RequestId']
136
+ # update the attributes hash if request is successful ...
137
+ self.attributes["#{opts[:name]}"] = "#{opts[:value]}" if response.response_header.status == 200
126
138
  EM.stop
127
139
  end
128
140
  }
@@ -134,7 +146,7 @@ class Topic
134
146
  raise InvalidOptions unless ( !(opts.empty?) && opts.instance_of?(Hash) )
135
147
  res=''
136
148
  params = {
137
- 'TopicArn' => "#{arn}",
149
+ 'TopicArn' => "#{@arn}",
138
150
  'Endpoint' => "#{opts[:endpoint]}",
139
151
  'Protocol' => "#{opts[:protocol]}",
140
152
  'Action' => 'Subscribe',
@@ -179,9 +191,9 @@ class Topic
179
191
 
180
192
  # grabs list of subscriptions for this topic only
181
193
  def subscriptions
182
- nh={}
194
+ res=nil
183
195
  params = {
184
- 'TopicArn' => "#{arn}",
196
+ 'TopicArn' => "#{@arn}",
185
197
  'Action' => 'ListSubscriptionsByTopic',
186
198
  'SignatureMethod' => 'HmacSHA256',
187
199
  'SignatureVersion' => 2,
@@ -195,23 +207,21 @@ class Topic
195
207
  arr = parsed_response['ListSubscriptionsByTopicResponse']['ListSubscriptionsByTopicResult']['Subscriptions']['member'] unless (parsed_response['ListSubscriptionsByTopicResponse']['ListSubscriptionsByTopicResult']['Subscriptions'].nil?)
196
208
 
197
209
  if !(arr.nil?) && (arr.instance_of?(Array))
198
- #temp fix for now
199
- nh = arr.inject({}) do |h,v|
200
- key = v["SubscriptionArn"].to_s
201
- value = v
202
- h[key.to_s] = value
203
- h
204
- end
210
+ res = arr
205
211
  elsif !(arr.nil?) && (arr.instance_of?(Hash))
206
212
  # to deal with one subscription issue
213
+ nh={}
207
214
  key = arr["SubscriptionArn"]
208
215
  arr.delete("SubscriptionArn")
209
216
  nh[key.to_s] = arr
217
+ res = nh
218
+ else
219
+ res = []
210
220
  end
211
221
  EM.stop
212
222
  end
213
223
  }
214
- nh
224
+ res
215
225
  end
216
226
 
217
227
  # The AddPermission action adds a statement to a topic's access control policy, granting access for the
@@ -221,7 +231,7 @@ class Topic
221
231
  raise InvalidOptions unless ( !(opts.empty?) && opts.instance_of?(Hash) )
222
232
  res=''
223
233
  params = {
224
- 'TopicArn' => "#{arn}",
234
+ 'TopicArn' => "#{@arn}",
225
235
  'Label' => "#{opts[:label]}",
226
236
  'ActionName.member.1' => "#{opts[:action_name]}",
227
237
  'AWSAccountId.member.1' => "#{opts[:account_id]}",
@@ -247,7 +257,7 @@ class Topic
247
257
  raise InvalidOptions unless ( !(label.empty?) && label.instance_of?(String) )
248
258
  res=''
249
259
  params = {
250
- 'TopicArn' => "#{arn}",
260
+ 'TopicArn' => "#{@arn}",
251
261
  'Label' => "#{label}",
252
262
  'Action' => 'RemovePermission',
253
263
  'SignatureMethod' => 'HmacSHA256',
@@ -272,7 +282,7 @@ class Topic
272
282
  res=''
273
283
  params = {
274
284
  'Subject' => subject,
275
- 'TopicArn' => "#{arn}",
285
+ 'TopicArn' => "#{@arn}",
276
286
  "Message" => "#{msg}",
277
287
  'Action' => 'Publish',
278
288
  'SignatureMethod' => 'HmacSHA256',
@@ -318,17 +328,16 @@ class Topic
318
328
 
319
329
 
320
330
 
321
-
322
- def make_hash(arr)
323
- hash = arr.inject({}) do |h, v|
324
- (v["key"] == "Policy")? value = JSON.parse(v["value"]) : value = v["value"]
325
- key = v["key"].to_s
326
- h[key] = value
327
- h
331
+ private
332
+ def make_hash(arr)
333
+ hash = arr.inject({}) do |h, v|
334
+ (v["key"] == "Policy")? value = JSON.parse(v["value"]) : value = v["value"]
335
+ key = v["key"].to_s
336
+ h[key] = value
337
+ h
338
+ end
339
+ hash
328
340
  end
329
-
330
- hash
331
- end
332
341
 
333
342
 
334
343
 
data/lib/amaze_sns.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  autoload 'Logger', 'logger'
2
2
 
3
-
4
3
  require File.dirname(__FILE__) + "/amaze/topic"
5
4
  require File.dirname(__FILE__) + "/amaze/subscription"
6
5
  require File.dirname(__FILE__) + "/amaze/helpers"
@@ -18,13 +17,19 @@ class AmazeSNS
18
17
  end
19
18
 
20
19
  class << self
21
- attr_accessor :host, :logger, :topics, :skey, :akey, :subscriptions
22
- #attr_writer :skey, :akey
20
+ attr_accessor :host, :topics, :skey, :akey, :subscriptions, :logger
21
+
22
+ def logger
23
+ @logger ||= begin
24
+ log = Logger.new(STDOUT)
25
+ log.level = Logger::INFO
26
+ log
27
+ end
28
+ end
23
29
  end
24
-
25
30
 
31
+
26
32
  self.host = 'sns.us-east-1.amazonaws.com'
27
- self.logger = Logger.new($STDOUT)
28
33
  self.skey = ''
29
34
  self.akey=''
30
35
  self.topics ||= {}
@@ -40,61 +45,59 @@ class AmazeSNS
40
45
  def self.method_missing(id, *args, &blk)
41
46
  case(id.to_s)
42
47
  when /^list_(.*)/
43
- send(:process_query, $1, &blk)
48
+ send(:process_query, $1, &Proc.new)
44
49
  when /^refresh_(.*)/
45
- send(:process_query, $1, &blk)
50
+ send(:process_query, $1)
46
51
  else
47
- #super
48
52
  raise NoMethodError
49
53
  end
50
54
  end
51
55
 
52
- def self.process_query(type, &blk)
53
- # p "INSIDE PROCESS query"
56
+ def self.process_query(type,&prc)
54
57
  type = type.capitalize
55
58
  params = {
56
59
  'Action' => "List#{type}",
57
60
  'SignatureMethod' => 'HmacSHA256',
58
61
  'SignatureVersion' => 2,
59
- 'Timestamp' => Time.now.iso8601,
62
+ 'Timestamp' => Time.now.iso8601, #Time.now.iso8601 makes tests fail
60
63
  'AWSAccessKeyId' => @akey
61
64
  }
62
65
 
63
- req_options={}
66
+ request = Request.new(params)
67
+ request.process
64
68
 
65
- if (blk)
66
- prc = blk
67
- else
68
- prc = default_prc
69
+ request.callback do |data|
70
+ yield data
69
71
  end
70
72
 
71
- req_options[:on_success] = prc
72
- #req = Request.new(params, req_options).process
73
-
74
- EM.run{
75
- Request.new(params, req_options).process
76
- }
73
+ request.errback do |resp|
74
+ puts "ERROR - #{resp.inspect}"
75
+ EM.stop
76
+ end
77
77
 
78
78
  end
79
79
 
80
80
  def self.default_prc
81
81
  prc = Proc.new do |resp|
82
82
  parsed_response = Crack::XML.parse(resp.response)
83
- #p "SUB RESPONSE: #{parsed_response.inspect}"
84
83
  self.process_response(parsed_response)
85
84
  EM.stop
86
85
  end
87
86
  end
88
87
 
88
+ def self.process_data(resp)
89
+ parsed_response = Crack::XML.parse(resp.response)
90
+ self.process_response(parsed_response)
91
+ EM.stop
92
+ end
93
+
89
94
  def self.process_response(resp)
90
95
  kind = (resp.has_key?("ListTopicsResponse"))? "Topics" : "Subscriptions"
91
96
  cla = (resp.has_key?("ListTopicsResponse"))? "Topic" : "Subscription"
92
- #p "KIND IS #{kind}"
93
-
97
+
94
98
  result = resp["List#{kind}Response"]["List#{kind}Result"]["#{kind}"]
95
99
  if result.nil?
96
- p "NO DATA FOUND"
97
- return nil
100
+ nil
98
101
  else
99
102
  results = result["member"]
100
103
  end
@@ -120,11 +123,9 @@ class AmazeSNS
120
123
  when "Topic"
121
124
  @collection[label].arn = t["TopicArn"]
122
125
  when "Subscription"
123
- #@collection[label] = t["TopicArn"]
124
126
  sub = Subscription.new(t)
125
127
  @collection[label] << sub unless @collection[label].detect{|x| x.subarn == sub.subarn}
126
128
  end
127
- #@collection[label].arn = t["TopicArn"]
128
129
  end
129
130
  end
130
131
  elsif (results.instance_of?(Hash))
@@ -136,11 +137,9 @@ class AmazeSNS
136
137
  when "Subscription"
137
138
  @collection[label] = Subscription.new(results)
138
139
  end
139
-
140
- #@collection[label] = Kernel.const_get("#{cla}").new(results)
141
140
  end
142
141
  else
143
- return nil
142
+ nil
144
143
  end # end outer if
145
144
  end
146
145