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