aliyun-mqs 0.0.3 → 0.1.1

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.
@@ -0,0 +1,56 @@
1
+ module Aliyun::Mqs
2
+ class Message
3
+
4
+ attr_reader :queue, :id, :body_md5, :body, :receipt_handle, :enqueue_at, :first_enqueue_at, :next_visible_at, :dequeue_count, :priority
5
+
6
+ def initialize queue, content
7
+ h = Hash.xml_object(content, "Message")
8
+ @queue = queue
9
+ @id = h["MessageId"]
10
+ @body_md5 = h["MessageBodyMD5"]
11
+ @body = h["MessageBody"]
12
+ @enqueue_at = Time.at(h["EnqueueTime"].to_i/1000.0)
13
+ @first_enqueue_at = Time.at(h["FirstDequeueTime"].to_i/1000.0)
14
+ @next_visible_at = Time.at(h["NextVisibleTime"].to_i/1000.0) if h["NextVisibleTime"]
15
+ @dequeue_count = h["DequeueCount"].to_i
16
+ @priority = h["Priority"].to_i
17
+ @receipt_handle = h["ReceiptHandle"]
18
+ end
19
+
20
+ def delete
21
+ check_receipt_handle
22
+ Aliyun::Mqs::Request.delete(queue.messages_path, params:{:ReceiptHandle => receipt_handle})
23
+ end
24
+
25
+ def change_visibility seconds
26
+ check_receipt_handle
27
+ Aliyun::Mqs::Request.put(queue.messages_path, params:{:ReceiptHandle => receipt_handle, :VisibilityTimeout=>seconds})
28
+ end
29
+
30
+ def to_s
31
+ s = {
32
+ "队列"=> queue.name,
33
+ "ID"=>id,
34
+ "MD5"=>body_md5,
35
+ "Receipt handle"=>receipt_handle,
36
+ "Enqueue at"=>enqueue_at,
37
+ "First enqueue at"=>first_enqueue_at,
38
+ "Next visible at"=>next_visible_at,
39
+ "Dequeue count" => dequeue_count,
40
+ "Priority"=>priority
41
+ }.collect{|k,v| "#{k}: #{v}"}
42
+
43
+ sep = "============================================="
44
+ s.unshift sep
45
+ s << sep
46
+ s << body
47
+ s.join("\n")
48
+ end
49
+
50
+ private
51
+ def check_receipt_handle
52
+ raise "No receipt handle for this operation" unless receipt_handle
53
+ end
54
+
55
+ end
56
+ end
@@ -1,77 +1,67 @@
1
- require 'active_support/core_ext/hash'
2
- require 'builder'
3
- require 'aliyun/mqs/http'
1
+ module Aliyun::Mqs
2
+ class Queue
3
+ attr_reader :name
4
4
 
5
- module Aliyun
6
- module Mqs
5
+ delegate :to_s, to: :name
7
6
 
8
- class Queue
9
- include Mqs::Http
10
-
11
- def initialize(name, access_owner_id: nil)
12
- @access_key_id = Mqs.configuration.access_key_id
13
- @access_key_secret = Mqs.configuration.access_key_secret
14
- @access_region = Mqs.configuration.access_region
15
- @access_owner_id = access_owner_id || Mqs.configuration.access_owner_id
16
- @access_queue = name
17
- @access_host = "#{@access_owner_id}.mqs-#{@access_region}.aliyuncs.com"
18
- throw '参数不能为nil' if instance_variables.any? {|x| x == nil}
7
+ class << self
8
+ def [] name
9
+ Queue.new(name)
19
10
  end
20
11
 
21
-
22
- def destroy
23
- verb = 'DELETE'
24
- request_resource = "/#{@access_queue}"
25
- request_uri = "http://#{@access_host}#{request_resource}"
26
- send_request(verb, request_uri)
12
+ def queues opts={}
13
+ mqs_options = {query: "x-mqs-prefix", offset: "x-mqs-marker", size: "x-mqs-ret-number"}
14
+ mqs_headers = opts.slice(*mqs_options.keys).reduce({}){|mqs_headers, item| k, v = *item; mqs_headers.merge!(mqs_options[k]=>v)}
15
+ response = Request.get("/", mqs_headers: mqs_headers)
16
+ Hash.xml_array(response, "Queues", "Queue").collect{|item| Queue.new(URI(item["QueueURL"]).path.sub!(/^\//, ""))}
27
17
  end
18
+ end
28
19
 
29
- def send(message_body, delay_seconds: 0, priority: 8)
30
- verb = 'POST'
31
- content_body = to_xml(message_body, delay_seconds, priority)
32
- request_resource = "/#{@access_queue}/messages"
33
- request_uri = "http://#{@access_host}#{request_resource}"
34
- send_request(verb, request_uri, content_body)
35
- end
20
+ def initialize name
21
+ @name = name
22
+ end
36
23
 
37
- def receive(waitseconds: nil, peekonly: false)
38
- verb = 'GET'
39
- query_params = {}
40
- query_params[:waitseconds] = waitseconds if waitseconds
41
- query_params[:peekonly] = true if peekonly # Aliyun doesn't accept uncessary query params
42
- request_resource = "/#{@access_queue}/messages" + (query_params.length > 0 ? '?' + query_params.to_param : '')
43
- request_uri = "http://#{@access_host}#{request_resource}"
44
- send_request(verb, request_uri)
24
+ def create opts={}
25
+ response = Request.put(queue_path) do |request|
26
+ msg_options = {
27
+ :VisibilityTimeout => 30,
28
+ :DelaySeconds => 0,
29
+ :MaximumMessageSize => 65536,
30
+ :MessageRetentionPeriod => 345600,
31
+ :PollingWaitSeconds => 0}.merge(opts)
32
+ request.content :Queue, msg_options
45
33
  end
34
+ end
46
35
 
47
- def delete message
48
- verb = 'DELETE'
49
- if String === message
50
- receipt_handle = message
51
- elsif Response === message
52
- receipt_handle = message.receipt_handle
53
- end
54
- request_resource = "/#{@access_queue}/messages?" + {ReceiptHandle: receipt_handle}.to_param
55
- request_uri = "http://#{@access_host}#{request_resource}"
56
- send_request(verb, request_uri)
57
- end
36
+ def delete
37
+ Request.delete(queue_path)
38
+ end
58
39
 
59
- def peek(waitseconcds: nil)
60
- receive(waitseconds: waitseconcds, peekonly: true)
40
+ def send_message message, opts={}
41
+ Request.post(messages_path) do |request|
42
+ msg_options = {:DelaySeconds => 0, :Priority => 10}.merge(opts)
43
+ request.content :Message, msg_options.merge(:MessageBody => message.to_s)
61
44
  end
45
+ end
62
46
 
63
- private
47
+ def receive_message wait_seconds: nil
48
+ request_opts = {}
49
+ request_opts.merge!(params:{waitseconds: wait_seconds}) if wait_seconds
50
+ result = Request.get(messages_path, request_opts)
51
+ Message.new(self, result)
52
+ end
64
53
 
65
- def to_xml(message_body, delay_seconds, priority)
66
- xml = Builder::XmlMarkup.new( :indent => 2 )
67
- xml.instruct! :xml, :encoding => 'UTF-8'
68
- xml.Message(:xmlns => 'http://mqs.aliyuncs.com/doc/v1/') do |m|
69
- m.MessageBody message_body
70
- m.DelaySeconds delay_seconds
71
- m.Priority priority
72
- end
73
- end
54
+ def peek_message
55
+ result = Request.get(messages_path, params: {peekonly: true})
56
+ Message.new(self, result)
57
+ end
58
+
59
+ def queue_path
60
+ "/#{name}"
61
+ end
74
62
 
63
+ def messages_path
64
+ "/#{name}/messages"
75
65
  end
76
66
 
77
67
  end
@@ -0,0 +1,88 @@
1
+ require 'base64'
2
+ module Aliyun::Mqs
3
+
4
+ class RequestException < Exception
5
+ attr_reader :content
6
+ delegate :[], to: :content
7
+
8
+ def initialize ex
9
+ @content = Hash.xml_object(ex.to_s, "Error")
10
+ rescue
11
+ @content = {"Message" => ex.message}
12
+ end
13
+ end
14
+
15
+ class Request
16
+ attr_reader :uri, :method, :date, :body, :content_md5, :content_type, :content_length, :mqs_headers
17
+ delegate :access_id, :key, :owner_id, :region, to: :configuration
18
+
19
+ class << self
20
+ [:get, :delete, :put, :post].each do |m|
21
+ define_method m do |*args, &block|
22
+ options = {method: m, path: args[0], mqs_headers: {}, params: {}}
23
+ options.merge!(args[1]) if args[1].is_a?(Hash)
24
+
25
+ request = Aliyun::Mqs::Request.new(options)
26
+ block.call(request) if block
27
+ request.execute
28
+ end
29
+ end
30
+ end
31
+
32
+ def initialize method: "get", path: "/", mqs_headers: {}, params: {}
33
+ conf = {
34
+ host: "#{owner_id}.mqs-#{region}.aliyuncs.com",
35
+ path: path
36
+ }
37
+ conf.merge!(query: params.to_query) unless params.empty?
38
+ @uri = URI::HTTP.build(conf)
39
+ @method = method
40
+ @mqs_headers = mqs_headers.merge("x-mqs-version" => "2014-07-08")
41
+ end
42
+
43
+ def content type, values={}
44
+ ns = "http://mqs.aliyuncs.com/doc/v1/"
45
+ builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
46
+ xml.send(type.to_sym, xmlns: ns) do |b|
47
+ values.each{|k,v| b.send k.to_sym, v}
48
+ end
49
+ end
50
+ @body = builder.to_xml
51
+ @content_md5 = Base64::encode64(Digest::MD5.hexdigest(body)).chop
52
+ @content_length = body.size
53
+ @content_type = "text/xml;charset=utf-8"
54
+ end
55
+
56
+ def execute
57
+ date = DateTime.now.httpdate
58
+ headers = {
59
+ "Authorization" => authorization(date),
60
+ "Content-Length" => content_length || 0,
61
+ "Content-Type" => content_type,
62
+ "Content-MD5" => content_md5,
63
+ "Date" => date,
64
+ "Host" => uri.host
65
+ }.merge(mqs_headers).reject{|k,v| v.nil?}
66
+ begin
67
+ RestClient.send *[method, uri.to_s, body, headers].compact
68
+ rescue RestClient::Exception => ex
69
+ raise RequestException.new(ex)
70
+ end
71
+ end
72
+
73
+ private
74
+ def configuration
75
+ Aliyun::Mqs.configuration
76
+ end
77
+
78
+ def authorization date
79
+ canonical_resource = [uri.path, uri.query].compact.join("?")
80
+ canonical_mq_headers = mqs_headers.sort.collect{|k,v| "#{k.downcase}:#{v}"}.join("\n")
81
+ method = self.method.to_s.upcase
82
+ signature = [method, content_md5 || "" , content_type || "" , date, canonical_mq_headers, canonical_resource].join("\n")
83
+ sha1 = Digest::HMAC.digest(signature, key, Digest::SHA1)
84
+ "MQS #{access_id}:#{Base64.encode64(sha1).chop}"
85
+ end
86
+
87
+ end
88
+ end
@@ -1,5 +1,5 @@
1
1
  module Aliyun
2
2
  module Mqs
3
- VERSION = "0.0.3"
3
+ VERSION = "0.1.1"
4
4
  end
5
5
  end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ describe Aliyun::Mqs::Queue do
4
+
5
+ let(:xml_message){
6
+ Aliyun::Mqs::Message.new(Aliyun::Mqs::Queue["aQueue"], <<-XML)
7
+ <?xml version="1.0" encoding="UTF-8" ?>
8
+ <Message xmlns="http://mqs.aliyuncs.com/doc/v1/">
9
+ <MessageId>5fea7756-0ea4-451a-a703-a558b933e274</MessageId>
10
+ <ReceiptHandle>MbZj6wDWli+QEauMZc8ZRv37sIW2iJKq3M9Mx/KSbkJ0</ReceiptHandle>
11
+ <MessageBodyMD5>fafb00f5732ab283681e124bf8747ed1</MessageBodyMD5>
12
+ <MessageBody>This is a test message</MessageBody>
13
+ <EnqueueTime>1250700979248000</EnqueueTime>
14
+ <NextVisibleTime>1250700799348000</NextVisibleTime>
15
+ <FirstDequeueTime>1250700779318000</FirstDequeueTime >
16
+ <DequeueCount>1</DequeueCount >
17
+ <Priority>8</Priority>
18
+ </Message>
19
+ XML
20
+ }
21
+
22
+ let(:peek_xml_message){
23
+ Aliyun::Mqs::Message.new(Aliyun::Mqs::Queue["aQueue"], <<-XML)
24
+ <?xml version="1.0" encoding="UTF-8" ?>
25
+ <Message xmlns="http://mqs.aliyuncs.com/doc/v1/">
26
+ <MessageId>5fea7756-0ea4-451a-a703-a558b933e274</MessageId>
27
+ <MessageBodyMD5>fafb00f5732ab283681e124bf8747ed1</MessageBodyMD5>
28
+ <MessageBody>This is a test message</MessageBody>
29
+ <EnqueueTime>1250700979248000</EnqueueTime>
30
+ <FirstDequeueTime>1250700979348000</FirstDequeueTime>
31
+ <DequeueCount>5</DequeueCount>
32
+ <Priority>8</Priority>
33
+ </Message>
34
+ XML
35
+ }
36
+
37
+ describe "#delete" do
38
+ specify "will delete the message from queue" do
39
+ expect(Aliyun::Mqs::Request).to receive(:delete).with("/aQueue/messages", params:{:ReceiptHandle=>"MbZj6wDWli+QEauMZc8ZRv37sIW2iJKq3M9Mx/KSbkJ0"})
40
+ xml_message.delete
41
+ end
42
+
43
+ specify "won't delete message without receipt_handle" do
44
+ expect{peek_xml_message.delete}.to raise_exception
45
+ end
46
+ end
47
+
48
+ describe "#change_visibility" do
49
+ specify "will change message's visibility timeout" do
50
+ expect(Aliyun::Mqs::Request).to receive(:put).with("/aQueue/messages", params:{
51
+ :ReceiptHandle=>"MbZj6wDWli+QEauMZc8ZRv37sIW2iJKq3M9Mx/KSbkJ0",
52
+ :VisibilityTimeout => 10
53
+ })
54
+
55
+ xml_message.change_visibility 10
56
+ end
57
+
58
+ specify "won't change message's visibility timeout given message has no receipt_handle" do
59
+ expect{peek_xml_message.change_visibility 10}.to raise_exception
60
+ end
61
+ end
62
+
63
+ end
@@ -0,0 +1,179 @@
1
+ require 'spec_helper'
2
+
3
+ describe Aliyun::Mqs::Queue do
4
+
5
+ specify ".[] will create new queue instance" do
6
+ queue = Aliyun::Mqs::Queue["aQueue"]
7
+ expect(queue).not_to be_nil
8
+ expect(queue.name).to eq("aQueue")
9
+ end
10
+
11
+ describe ".queues" do
12
+ let(:xml_response){
13
+ <<-XML
14
+ <?xml version="1.0"?>
15
+ <Queues xmlns="http://mqs.aliyuncs.com/doc/v1">
16
+ <Queue>
17
+ <QueueURL>http://xxxxx.mqs-cn-hangzhou.aliyuncs.com/test</QueueURL>
18
+ </Queue>
19
+ </Queues>
20
+ XML
21
+ }
22
+
23
+ specify "find all queues" do
24
+ expect(Aliyun::Mqs::Request).to receive(:get).with("/", mqs_headers:{}).and_return xml_response
25
+ queues = Aliyun::Mqs::Queue.queues
26
+ expect(queues.size).to eq(1)
27
+ expect(queues[0].name).to eq("test")
28
+ end
29
+
30
+ specify "query queues" do
31
+ expect(Aliyun::Mqs::Request).to receive(:get).with("/", mqs_headers:{"x-mqs-prefix"=>"query"}).and_return xml_response
32
+ queues = Aliyun::Mqs::Queue.queues(query: "query")
33
+ end
34
+
35
+ specify "find number of queues" do
36
+ expect(Aliyun::Mqs::Request).to receive(:get).with("/", mqs_headers:{"x-mqs-ret-number"=>5}).and_return xml_response
37
+ queues = Aliyun::Mqs::Queue.queues(size: 5)
38
+ end
39
+
40
+ specify "find of queues start at given position" do
41
+ expect(Aliyun::Mqs::Request).to receive(:get).with("/", mqs_headers:{"x-mqs-marker"=>2}).and_return xml_response
42
+ queues = Aliyun::Mqs::Queue.queues(offset: 2)
43
+ end
44
+ end
45
+
46
+
47
+ describe "#create" do
48
+ specify "will create a new queue with default options" do
49
+ expect(RestClient).to receive(:put) do |*args|
50
+ path, body, headers = *args
51
+ expect(path).to eq("http://owner-id.mqs-region.aliyuncs.com/aQueue")
52
+ xml = Hash.from_xml(body)
53
+ expect(xml["Queue"]["VisibilityTimeout"]).to eq("30")
54
+ expect(xml["Queue"]["DelaySeconds"]).to eq("0")
55
+ expect(xml["Queue"]["MaximumMessageSize"]).to eq("65536")
56
+ expect(xml["Queue"]["MessageRetentionPeriod"]).to eq("345600")
57
+ expect(xml["Queue"]["PollingWaitSeconds"]).to eq("0")
58
+ expect(headers).not_to be_nil
59
+ end
60
+ Aliyun::Mqs::Queue["aQueue"].create
61
+ end
62
+
63
+ specify "will create a new queue with customized options" do
64
+ expect(RestClient).to receive(:put) do |*args|
65
+ path, body, headers = *args
66
+ expect(Hash.from_xml(body)["Queue"]["PollingWaitSeconds"]).to eq("30")
67
+ end
68
+ Aliyun::Mqs::Queue["aQueue"].create(:PollingWaitSeconds => 30)
69
+ end
70
+ end
71
+
72
+ describe "#delete" do
73
+ specify "will delete existing queue" do
74
+ expect(Aliyun::Mqs::Request).to receive(:delete).with("/aQueue")
75
+ Aliyun::Mqs::Queue["aQueue"].delete
76
+ end
77
+ end
78
+
79
+ describe "#send_message" do
80
+ specify "will send a message to a queue with default options" do
81
+ expect(RestClient).to receive(:post) do |*args|
82
+ path, body, headers = *args
83
+ expect(path).to eq("http://owner-id.mqs-region.aliyuncs.com/aQueue/messages")
84
+ xml = Hash.from_xml(body)
85
+ expect(xml["Message"]["MessageBody"]).to eq("text message")
86
+ expect(xml["Message"]["DelaySeconds"]).to eq("0")
87
+ expect(xml["Message"]["Priority"]).to eq("10")
88
+ expect(headers).not_to be_nil
89
+ end
90
+
91
+ Aliyun::Mqs::Queue["aQueue"].send_message "text message"
92
+ end
93
+
94
+
95
+ specify "will send a message to a queue with customized options" do
96
+ expect(RestClient).to receive(:post) do |*args|
97
+ path, body, headers = *args
98
+ expect(Hash.from_xml(body)["Message"]["Priority"]).to eq("1")
99
+ end
100
+
101
+ Aliyun::Mqs::Queue["aQueue"].send_message "text message", :Priority=>1
102
+ end
103
+ end
104
+
105
+
106
+ describe "#receive_message" do
107
+ let(:xml_response){
108
+ <<-XML
109
+ <?xml version="1.0" encoding="UTF-8" ?>
110
+ <Message xmlns="http://mqs.aliyuncs.com/doc/v1/">
111
+ <MessageId>5fea7756-0ea4-451a-a703-a558b933e274</MessageId>
112
+ <ReceiptHandle>MbZj6wDWli+QEauMZc8ZRv37sIW2iJKq3M9Mx/KSbkJ0</ReceiptHandle>
113
+ <MessageBodyMD5>fafb00f5732ab283681e124bf8747ed1</MessageBodyMD5>
114
+ <MessageBody>This is a test message</MessageBody>
115
+ <EnqueueTime>1250700979248000</EnqueueTime>
116
+ <NextVisibleTime>1250700799348000</NextVisibleTime>
117
+ <FirstDequeueTime>1250700779318000</FirstDequeueTime >
118
+ <DequeueCount>1</DequeueCount >
119
+ <Priority>8</Priority>
120
+ </Message>
121
+ XML
122
+ }
123
+
124
+ specify "will receive message from a queue" do
125
+ expect(Aliyun::Mqs::Request).to receive(:get).with("/aQueue/messages",{}).and_return xml_response
126
+
127
+ message = Aliyun::Mqs::Queue["aQueue"].receive_message
128
+ expect(message).not_to be_nil
129
+ expect(message.id).to eq("5fea7756-0ea4-451a-a703-a558b933e274")
130
+ expect(message.body).to eq("This is a test message")
131
+ expect(message.body_md5).to eq("fafb00f5732ab283681e124bf8747ed1")
132
+ expect(message.receipt_handle).to eq("MbZj6wDWli+QEauMZc8ZRv37sIW2iJKq3M9Mx/KSbkJ0")
133
+ expect(message.enqueue_at).to eq(Time.at(1250700979248000/1000.0))
134
+ expect(message.first_enqueue_at).to eq(Time.at(1250700779318000/1000.0))
135
+ expect(message.next_visible_at).to eq(Time.at(1250700799348000/1000.0))
136
+ expect(message.dequeue_count).to eq(1)
137
+ expect(message.priority).to eq(8)
138
+ end
139
+
140
+ specify "will receive message from a queue with poll wait" do
141
+ expect(Aliyun::Mqs::Request).to receive(:get).with("/aQueue/messages",params:{waitseconds: 60}).and_return xml_response
142
+ message = Aliyun::Mqs::Queue["aQueue"].receive_message wait_seconds: 60
143
+ end
144
+ end
145
+
146
+ describe "#peek" do
147
+ let(:xml_response){
148
+ <<-XML
149
+ <?xml version="1.0" encoding="UTF-8" ?>
150
+ <Message xmlns="http://mqs.aliyuncs.com/doc/v1/">
151
+ <MessageId>5fea7756-0ea4-451a-a703-a558b933e274</MessageId>
152
+ <MessageBodyMD5>fafb00f5732ab283681e124bf8747ed1</MessageBodyMD5>
153
+ <MessageBody>This is a test message</MessageBody>
154
+ <EnqueueTime>1250700979248000</EnqueueTime>
155
+ <FirstDequeueTime>1250700979348000</FirstDequeueTime>
156
+ <DequeueCount>5</DequeueCount>
157
+ <Priority>8</Priority>
158
+ </Message>
159
+ XML
160
+ }
161
+
162
+ specify "will peek message of a queue" do
163
+ expect(Aliyun::Mqs::Request).to receive(:get).with("/aQueue/messages",params:{peekonly: true}).and_return xml_response
164
+ message = Aliyun::Mqs::Queue["aQueue"].peek_message
165
+
166
+ expect(message).not_to be_nil
167
+ expect(message.id).to eq("5fea7756-0ea4-451a-a703-a558b933e274")
168
+ expect(message.body).to eq("This is a test message")
169
+ expect(message.body_md5).to eq("fafb00f5732ab283681e124bf8747ed1")
170
+ expect(message.receipt_handle).to be_nil
171
+ expect(message.enqueue_at).to eq(Time.at(1250700979248000/1000.0))
172
+ expect(message.first_enqueue_at).to eq(Time.at(1250700979348000/1000.0))
173
+ expect(message.next_visible_at).to be_nil
174
+ expect(message.dequeue_count).to eq(5)
175
+ expect(message.priority).to eq(8)
176
+ end
177
+ end
178
+
179
+ end