lsqs 0.0.1

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: 7ffe37c92591bcb25697b60e65c4786ec81443ad
4
+ data.tar.gz: 3030401e3eea31364806a2c77412381e69eba483
5
+ SHA512:
6
+ metadata.gz: bd0452e069a1c855dd6439e9076e81cb1a213a53b3741f4c001c39aa7111676beb3a23ecf957068646a091e71c4022bdfba445940c0e55b3b89b5afa8e4454dc
7
+ data.tar.gz: 2ebb96c28769c1df0b2b21a43595eea9ba1a9687ec837bded0ec8a0604adf8bf889e36fb3099a3d2f33f4c8385c07390283b2e6f5000e1298322f03dd6b0bf47
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ yardoc
2
+ Gemfile.lock
3
+ pkg
4
+ coverage
5
+ log/*.log
6
+ .DS_Store
7
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in lsqs.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 giannismelidis
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # README
2
+
3
+ This is a Sinatra server (using Puma) that mimics basic functionality of the
4
+ Amazon SQS Service (for development purposes).
5
+
6
+ The messages are stored in hashes therefore they are not persisted. When you
7
+ shut down the server, the messages are lost.
8
+
9
+ There is no authentication mechanism in place (yet), so no access key id and
10
+ secret key are needed.
11
+
12
+ At the moment these types of transactions are implemented:
13
+ `CreateQueue`
14
+ `PurgeQueue`
15
+ `ListQueues`
16
+ `DeleteQueue`
17
+ `GetQueueUrl`
18
+ `ReceiveMessage`
19
+ `SendMessage`
20
+ `SendMessageBatch`
21
+ `DeleteMessage`
22
+ `DeleteMessageBatch`
23
+ `ChangeMessageVisibility`
24
+
25
+ ## Requirements
26
+
27
+ * Ruby 2.2 or newer
28
+
29
+ ## Installation
30
+
31
+ Just install the gem:
32
+
33
+ gem install lsqs
34
+
35
+ It doesn't require the `aws-sdk` itself.
36
+
37
+ ## Usage
38
+
39
+ Run `lsqs-server` in the console.
40
+
41
+ In your application set the AWS configuration like this (change `base_url` and
42
+ `port` number if you are not using the default ones):
43
+
44
+ ```
45
+
46
+ require 'aws-sdk' # version 2
47
+
48
+ # it requires a dot in the URI when polling (to retrieve the region)
49
+ # so 'localhost' won't work.
50
+ base_url = '127.0.0.1'
51
+ port = 9292
52
+
53
+ Aws.config.update(:endpoint => "http://#{base_url}:#{port}")
54
+ ```
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
data/bin/console ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/lsqs'
4
+ require 'pry'
5
+
6
+ Pry.start
data/bin/lsqs-server ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require File.expand_path('../../lib/lsqs', __FILE__)
3
+
4
+ Puma::CLI.new(ARGV).run
@@ -0,0 +1,9 @@
1
+ <xml>
2
+ <{{action}}Result>
3
+ {{content}}
4
+ </{{action}}Result>
5
+ <ResponseMetadata>
6
+ <RequestId>{{request_id}}</RequestId>
7
+ </ResponseMetadata>
8
+ {{error}}
9
+ </xml>
data/config.ru ADDED
@@ -0,0 +1,4 @@
1
+ require File.expand_path('../lib/lsqs', __FILE__)
2
+ require File.expand_path('../lib/lsqs/server', __FILE__)
3
+
4
+ run LSQS::Server
@@ -0,0 +1,35 @@
1
+ module LSQS
2
+ class ActionRouter
3
+ class ActionError < NameError; end
4
+ attr_reader :queue_list
5
+
6
+ ##
7
+ # @param [LSQS::QueueList] queue_list
8
+ #
9
+ def initialize(queue_list)
10
+ @queue_list = queue_list
11
+ end
12
+
13
+ ##
14
+ # Distributes an action to the appropriate class. If an action does not
15
+ # exist, it throws an error.
16
+ #
17
+ # @param [String] action_name
18
+ # @param [Hash] options
19
+ #
20
+ # @return [LSQS::Actions::Base]
21
+ #
22
+ def distribute(action_name, options)
23
+ if LSQS::Actions.const_defined?(action_name)
24
+ action = LSQS::Actions.const_get(action_name).new(queue_list)
25
+ queue_list.query do
26
+ action.perform(options)
27
+ end
28
+
29
+ return action
30
+ else
31
+ raise ActionError, "undefined action `#{action_name}`"
32
+ end
33
+ end
34
+ end # ActionRouter
35
+ end # LSQS
@@ -0,0 +1,51 @@
1
+ module LSQS
2
+ module Actions
3
+ class Base
4
+ attr_accessor :queue_list
5
+ attr_reader :builder
6
+
7
+ def initialize(queue_list)
8
+ @queue_list = queue_list
9
+ end
10
+
11
+ ##
12
+ # Returns the name of the class without namespacing.
13
+ #
14
+ # @return [String]
15
+ #
16
+ def name
17
+ return self.class.name.split('::').last
18
+ end
19
+
20
+ ##
21
+ # Initializes a XML builder.
22
+ #
23
+ # @return [Builder::XmlMarkup]
24
+ #
25
+ def builder
26
+ @builder ||= Builder::XmlMarkup.new(:indent => 2)
27
+ end
28
+
29
+ ##
30
+ # Outputs XML from the builder as a string.
31
+ #
32
+ # @return [String]
33
+ #
34
+ def to_xml
35
+ builder.target!
36
+ end
37
+
38
+ ##
39
+ # Builds the URL of the queue.
40
+ #
41
+ # @param [String] base_url
42
+ # @param [String] queue_name
43
+
44
+ # @return [String]
45
+ #
46
+ def build_url(base_url, queue_name)
47
+ return "#{base_url}/#{queue_name}"
48
+ end
49
+ end # Base
50
+ end # Actions
51
+ end # LSQS
@@ -0,0 +1,23 @@
1
+ module LSQS
2
+ module Actions
3
+ class ChangeMessageVisibility < Base
4
+ ##
5
+ # Performs the specific action.
6
+ #
7
+ # @param [Hash] params
8
+ #
9
+ # @return [String]
10
+ #
11
+ def perform(params)
12
+ name = params['QueueName']
13
+ visibility = params['VisibilityTimeout']
14
+ receipt = params['ReceiptHandle']
15
+ queue = queue_list.find(name)
16
+
17
+ queue.change_message_visibility(receipt, visibility.to_i)
18
+
19
+ return
20
+ end
21
+ end # ChangeMessageVisibility
22
+ end # Actions
23
+ end # LSQS
@@ -0,0 +1,22 @@
1
+ module LSQS
2
+ module Actions
3
+ class CreateQueue < Base
4
+ ##
5
+ # Performs the specific action.
6
+ #
7
+ # @param [Hash] params
8
+ #
9
+ # @return [String]
10
+ #
11
+ def perform(params)
12
+ name = params['QueueName']
13
+ queue = queue_list.create(name, params)
14
+
15
+ base_url = params['base_url']
16
+ url = build_url(base_url, name)
17
+
18
+ builder.QueueUrl build_url(base_url, name)
19
+ end
20
+ end # CreateQueue
21
+ end # Actions
22
+ end # LSQS
@@ -0,0 +1,22 @@
1
+ module LSQS
2
+ module Actions
3
+ class DeleteMessage < Base
4
+ ##
5
+ # Performs the specific action.
6
+ #
7
+ # @param [Hash] params
8
+ #
9
+ # @return [String]
10
+ #
11
+ def perform(params)
12
+ name = params['QueueName']
13
+
14
+ queue = queue_list.find(name)
15
+
16
+ receipt = params['ReceiptHandle']
17
+ queue.delete_message(receipt)
18
+ return
19
+ end
20
+ end # DeleteMessage
21
+ end # Actions
22
+ end # LSQS
@@ -0,0 +1,36 @@
1
+ module LSQS
2
+ module Actions
3
+ class DeleteMessageBatch < Base
4
+ ##
5
+ # Performs the specific action.
6
+ #
7
+ # @param [Hash] params
8
+ #
9
+ # @return [String]
10
+ #
11
+ def perform(params)
12
+ name = params['QueueName']
13
+ queue = queue_list.find(name)
14
+
15
+ queue = queue_list.find(name)
16
+ receipts = params.select do |key, value|
17
+ key.match(/DeleteMessageBatchRequestEntry\.\d+\.ReceiptHandle/)
18
+ end
19
+
20
+ deleted = []
21
+
22
+ receipts.each do |key, value|
23
+ id = key.split('.')[1]
24
+ queue.delete_message(value)
25
+ deleted << params["DeleteMessageBatchRequestEntry.#{id}.Id"]
26
+ end
27
+
28
+ deleted.compact.each do |id|
29
+ builder.DeleteMessageBatchResultEntry do
30
+ builder.Id id
31
+ end
32
+ end
33
+ end
34
+ end # DeleteMessageBatch
35
+ end # Actions
36
+ end # LSQS
@@ -0,0 +1,18 @@
1
+ module LSQS
2
+ module Actions
3
+ class DeleteQueue < Base
4
+ ##
5
+ # Performs the specific action.
6
+ #
7
+ # @param [Hash] params
8
+ #
9
+ # @return [String]
10
+ #
11
+ def perform(params)
12
+ name = params['QueueName']
13
+ queue = queue_list.delete(name)
14
+ return
15
+ end
16
+ end # DeleteQueue
17
+ end # Actions
18
+ end # LSQS
@@ -0,0 +1,23 @@
1
+ module LSQS
2
+ module Actions
3
+ class GetQueueUrl < Base
4
+ ##
5
+ # Performs the specific action.
6
+ #
7
+ # @param [Hash] params
8
+ #
9
+ # @return [String]
10
+ #
11
+ def perform(params)
12
+ base_url = params['base_url']
13
+ name = params['QueueName']
14
+
15
+ queue = queue_list.delete(name)
16
+
17
+ url = build_url(base_url, name)
18
+
19
+ builder.QueueUrl build_url(base_url, name)
20
+ end
21
+ end # GetQueueUrl
22
+ end # Actions
23
+ end # LSQS
@@ -0,0 +1,22 @@
1
+ module LSQS
2
+ module Actions
3
+ class ListQueues < Base
4
+ ##
5
+ # Performs the specific action.
6
+ #
7
+ # @param [Hash] params
8
+ #
9
+ # @return [String]
10
+ #
11
+ def perform(params)
12
+ base_url = params['base_url']
13
+
14
+ queues = queue_list.inspect(params)
15
+
16
+ queues.each do |queue|
17
+ builder.QueueUrl build_url(base_url, queue)
18
+ end
19
+ end
20
+ end # ListQueues
21
+ end # Actions
22
+ end # LSQS
@@ -0,0 +1,19 @@
1
+ module LSQS
2
+ module Actions
3
+ class PurgeQueue < Base
4
+ ##
5
+ # Performs the specific action.
6
+ #
7
+ # @param [Hash] params
8
+ #
9
+ # @return [String]
10
+ #
11
+ def perform(params)
12
+ name = params['QueueName']
13
+ queue = queue_list.find(name)
14
+ queue.purge
15
+ return
16
+ end
17
+ end # PurgeQueue
18
+ end # Actions
19
+ end # LSQS
@@ -0,0 +1,27 @@
1
+ module LSQS
2
+ module Actions
3
+ class ReceiveMessage < Base
4
+ ##
5
+ # Performs the specific action.
6
+ #
7
+ # @param [Hash] params
8
+ #
9
+ # @return [String]
10
+ #
11
+ def perform(params)
12
+ name = params['QueueName']
13
+ queue = queue_list.find(name)
14
+ messages = queue.get_messages(params)
15
+
16
+ messages.each do |receipt, message|
17
+ builder.Message do
18
+ builder.MessageId message.id
19
+ builder.ReceiptHandle receipt
20
+ builder.MD5OfBody message.md5
21
+ builder.Body message.body
22
+ end
23
+ end
24
+ end
25
+ end # ReceiveMessage
26
+ end # Actions
27
+ end # LSQS
@@ -0,0 +1,21 @@
1
+ module LSQS
2
+ module Actions
3
+ class SendMessage < Base
4
+ ##
5
+ # Performs the specific action.
6
+ #
7
+ # @param [Hash] params
8
+ #
9
+ # @return [String]
10
+ #
11
+ def perform(params)
12
+ name = params['QueueName']
13
+ queue = queue_list.find(name)
14
+ message = queue.create_message(params)
15
+
16
+ builder.MD5OfMessageBody message.md5
17
+ builder.MessageId message.id
18
+ end
19
+ end # SendMessage
20
+ end # Actions
21
+ end # LSQS
@@ -0,0 +1,42 @@
1
+ module LSQS
2
+ module Actions
3
+ class SendMessageBatch < Base
4
+ ##
5
+ # Performs the specific action.
6
+ #
7
+ # @param [Hash] params
8
+ #
9
+ # @return [String]
10
+ #
11
+ def perform(params)
12
+ name = params['QueueName']
13
+ queue = queue_list.find(name)
14
+
15
+ messages = params.select do |key, value|
16
+ key.match(/SendMessageBatchRequestEntry\.\d+\.MessageBody/)
17
+ end
18
+
19
+ result = {}
20
+
21
+ messages.each do |key, value|
22
+ id = key.split('.')[1]
23
+ msg_id = params["SendMessageBatchRequestEntry.#{id}.Id"]
24
+ delay = params["SendMessageBatchRequestEntry.#{id}.DelaySeconds"]
25
+ message = queue.create_message(
26
+ 'MessageBody' => value,
27
+ 'DelaySeconds' => delay
28
+ )
29
+ result[msg_id] = message
30
+ end
31
+
32
+ result.each do |msg_id, message|
33
+ builder.SendMessageBatchResultEntry do
34
+ builder.MD5OfMessageBody message.md5
35
+ builder.MessageId message.id
36
+ builder.Id msg_id
37
+ end
38
+ end
39
+ end
40
+ end # SendMessageBatch
41
+ end # Actions
42
+ end # LSQS
@@ -0,0 +1,65 @@
1
+ module LSQS
2
+ class ErrorHandler
3
+ ERROR_LIST = {
4
+ 'AccessDenied' => 403,
5
+ 'AuthFailure' => 401,
6
+ 'ConflictingQueryParameter' => 400,
7
+ 'InternalError' => 500,
8
+ 'InvalidAccessKeyId' => 401,
9
+ 'InvalidAction' => 400,
10
+ 'InvalidAddress' => 404,
11
+ 'InvalidAttributeName' => 400,
12
+ 'InvalidHttpRequest' => 400,
13
+ 'InvalidMessageContents' => 400,
14
+ 'InvalidParameterCombination' => 400,
15
+ 'InvalidParameterValue' => 400,
16
+ 'InvalidQueryParameter' => 400,
17
+ 'InvalidRequest' => 400,
18
+ 'InvalidSecurity' => 403,
19
+ 'InvalidSecurityToken' => 400,
20
+ 'MalformedVersion' => 400,
21
+ 'MessageTooLong' => 400,
22
+ 'MessageNotInflight' => 400,
23
+ 'MissingClientTokenId' => 403,
24
+ 'MissingCredentials' => 401,
25
+ 'MissingParameter' => 400,
26
+ 'NoSuchVersion' => 400,
27
+ 'NonExistentQueue' => 400,
28
+ 'NotAuthorizedToUseVersion' => 401,
29
+ 'QueueDeletedRecently' => 400,
30
+ 'QueueNameExists' => 400,
31
+ 'ReadCountOutOfRange' => 400,
32
+ 'ReceiptHandleIsInvalid' => 400,
33
+ 'RequestExpired' => 400,
34
+ 'RequestThrottled' => 403,
35
+ 'ServiceUnavailable' => 503,
36
+ 'X509ParseError' => 400
37
+ }
38
+
39
+ def initialize(app)
40
+ @app = app
41
+ end
42
+
43
+ def call(env)
44
+ begin
45
+ @app.call env
46
+ rescue => error
47
+ xml = Builder::XmlMarkup.new(:index => 2)
48
+
49
+ status = ERROR_LIST[error.message] || 500
50
+
51
+
52
+ xml.ErrorResponse do
53
+ xml.Error do
54
+ xml.Type error.message
55
+ xml.Code status
56
+ xml.Message error.to_s
57
+ xml.Detail
58
+ end
59
+ end
60
+
61
+ [status, {}, [LSQS.template.render_error(xml)]]
62
+ end
63
+ end
64
+ end # ErrorHandler
65
+ end # LSQS
@@ -0,0 +1,49 @@
1
+ module LSQS
2
+ class Message
3
+ attr_reader :body, :id, :md5
4
+ attr_accessor :visibility_timeout
5
+
6
+ def initialize(options = {})
7
+ @body = options['MessageBody'] || ''
8
+ @id = options.fetch('Id') { SecureRandom.uuid }
9
+ @md5 = options.fetch('MD5') { Digest::MD5.hexdigest(body) }
10
+ end
11
+
12
+ def attributes
13
+ {
14
+ "MessageBody" => body,
15
+ "Id" => id,
16
+ "MD5" => md5,
17
+ }
18
+ end
19
+
20
+ ##
21
+ # Check if a message's visibility has timed out.
22
+ #
23
+ # @return [TrueClass|FalseClass]
24
+ #
25
+ def expired?
26
+ visibility_timeout.nil? || visibility_timeout < Time.now
27
+ end
28
+
29
+ ##
30
+ # Sets the time when the message should expire.
31
+ #
32
+ # @param [Fixnum] seconds
33
+ #
34
+ # @return [Time]
35
+ #
36
+ def expire_in(seconds)
37
+ @visibility_timeout = Time.now + seconds
38
+ end
39
+
40
+ ##
41
+ # Resets the visibility time of the message.
42
+ #
43
+ # @return [NilClass]
44
+ #
45
+ def expire
46
+ @visibility_timeout = nil
47
+ end
48
+ end # Message
49
+ end # LSQS