pheme 3.4.0 → 4.0.5
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 +4 -4
- data/.github/workflows/main.yml +32 -0
- data/.gitignore +0 -1
- data/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +24 -17
- data/CHANGELOG.md +24 -0
- data/Gemfile.lock +138 -0
- data/README.md +10 -1
- data/catalog-info.yaml +12 -0
- data/lib/pheme/configuration.rb +1 -2
- data/lib/pheme/message_handler.rb +3 -2
- data/lib/pheme/queue_poller.rb +43 -18
- data/lib/pheme/version.rb +1 -1
- data/pheme.gemspec +1 -2
- data/sonar-project.properties +4 -0
- data/spec/queue_poller_spec.rb +69 -10
- data/spec/spec_helper.rb +0 -5
- data/spec/support/example_queue_poller.rb +2 -2
- data/spec/support/example_queue_poller_with_inlined_handler.rb +2 -1
- data/spec/version_spec.rb +6 -8
- metadata +8 -20
- data/.circleci/config.yml +0 -149
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2800b7391329c041eb8ab2e0253e3708f18cacb21458bbc210aa83e9b886685d
|
4
|
+
data.tar.gz: c3115082962233b0ae03a3b9f1ed6d67dd82e18d52417d1959b615f8ad074569
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 185617f33efa34f653fd9aa203ebf83d2f68ec5e9ce30d841eff1e47b5a7ae001af60a8b686b1793cebcb456e4630d566b55a6f6f3ed2cc79e5d292ffc8404b7
|
7
|
+
data.tar.gz: 7c86ed02c4efd79e1fac0464eaffe1b7ac891d9d5b842b8ae38a0e8b17ca8934acf9ff12a8cb53ff156999c83dd63ce55bdf71c70d384bc9312f726782e29ea4
|
@@ -0,0 +1,32 @@
|
|
1
|
+
---
|
2
|
+
name: Pipeline
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
name: Build
|
8
|
+
runs-on: ubuntu-20.04
|
9
|
+
steps:
|
10
|
+
- uses: actions/checkout@v2
|
11
|
+
- uses: ruby/setup-ruby@v1
|
12
|
+
with:
|
13
|
+
bundler-cache: true
|
14
|
+
- name: Lint
|
15
|
+
run: bundle exec rubocop
|
16
|
+
- name: Test
|
17
|
+
run: bundle exec rspec
|
18
|
+
- name: Release the gem
|
19
|
+
if: ${{ github.ref == 'refs/heads/main' }}
|
20
|
+
run: |
|
21
|
+
mkdir -p ~/.gem
|
22
|
+
cat << EOF > ~/.gem/credentials
|
23
|
+
---
|
24
|
+
:github: Bearer ${GITHUB_TOKEN}
|
25
|
+
:rubygems_api_key: ${RUBYGEMS_API_KEY}
|
26
|
+
EOF
|
27
|
+
chmod 0600 ~/.gem/credentials
|
28
|
+
git config user.email "noreply@wealthsimple.com"
|
29
|
+
git config user.name "Wolfbot"
|
30
|
+
bundle exec rake release
|
31
|
+
env:
|
32
|
+
RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/.rubocop_todo.yml
CHANGED
@@ -1,42 +1,42 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on
|
3
|
+
# on 2021-01-07 16:23:09 UTC using RuboCop version 1.6.1.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
8
8
|
|
9
|
+
# Offense count: 19
|
10
|
+
# Cop supports --auto-correct.
|
11
|
+
# Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
12
|
+
# URISchemes: http, https
|
13
|
+
Layout/LineLength:
|
14
|
+
Max: 147
|
15
|
+
|
9
16
|
# Offense count: 2
|
17
|
+
# Configuration parameters: IgnoredMethods, CountRepeatedAttributes.
|
10
18
|
Metrics/AbcSize:
|
11
19
|
Max: 22
|
12
20
|
|
13
21
|
# Offense count: 1
|
14
|
-
# Configuration parameters:
|
15
|
-
Metrics/ClassLength:
|
16
|
-
Max: 164
|
17
|
-
|
18
|
-
# Offense count: 1
|
22
|
+
# Configuration parameters: IgnoredMethods.
|
19
23
|
Metrics/CyclomaticComplexity:
|
20
24
|
Max: 10
|
21
25
|
|
22
|
-
# Offense count: 17
|
23
|
-
# Cop supports --auto-correct.
|
24
|
-
# Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
25
|
-
# URISchemes: http, https
|
26
|
-
Metrics/LineLength:
|
27
|
-
Max: 147
|
28
|
-
|
29
26
|
# Offense count: 3
|
30
|
-
# Configuration parameters: CountComments, ExcludedMethods.
|
27
|
+
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods.
|
28
|
+
# IgnoredMethods: extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended, extended
|
31
29
|
Metrics/MethodLength:
|
32
30
|
Max: 28
|
33
31
|
|
34
32
|
# Offense count: 1
|
33
|
+
# Configuration parameters: IgnoredMethods.
|
35
34
|
Metrics/PerceivedComplexity:
|
36
35
|
Max: 11
|
37
36
|
|
38
|
-
# Offense count:
|
39
|
-
# Configuration parameters: CustomTransform, IgnoreMethods.
|
37
|
+
# Offense count: 9
|
38
|
+
# Configuration parameters: Include, CustomTransform, IgnoreMethods, SpecSuffixOnly.
|
39
|
+
# Include: **/*_spec*rb*, **/spec/**/*
|
40
40
|
RSpec/FilePath:
|
41
41
|
Exclude:
|
42
42
|
- 'spec/configuration_spec.rb'
|
@@ -45,8 +45,8 @@ RSpec/FilePath:
|
|
45
45
|
- 'spec/message_type/aws_event_spec.rb'
|
46
46
|
- 'spec/message_type/sns_message_spec.rb'
|
47
47
|
- 'spec/queue_poller_spec.rb'
|
48
|
-
- 'spec/topic_publisher_spec.rb'
|
49
48
|
- 'spec/rollbar_spec.rb'
|
49
|
+
- 'spec/topic_publisher_spec.rb'
|
50
50
|
- 'spec/version_spec.rb'
|
51
51
|
|
52
52
|
# Offense count: 14
|
@@ -70,3 +70,10 @@ RSpec/VerifiedDoubles:
|
|
70
70
|
Style/GuardClause:
|
71
71
|
Exclude:
|
72
72
|
- 'lib/pheme/queue_poller.rb'
|
73
|
+
|
74
|
+
# Offense count: 2
|
75
|
+
# Cop supports --auto-correct.
|
76
|
+
Style/IfUnlessModifier:
|
77
|
+
Exclude:
|
78
|
+
- 'lib/pheme/queue_poller.rb'
|
79
|
+
- 'pheme.gemspec'
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,30 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
+
## 4.0.5 - 2021-04-20
|
8
|
+
### Changed
|
9
|
+
- Update dependencies
|
10
|
+
|
11
|
+
## 4.0.4 - 2021-04-16
|
12
|
+
### Changed
|
13
|
+
- Migrate CI from CircleCI to GitHub Actions
|
14
|
+
|
15
|
+
## 4.0.3 - 2021-03-08
|
16
|
+
- Update documentation to include instructions on directly publishing to SQS
|
17
|
+
|
18
|
+
## 4.0.2 - 2020-12-17
|
19
|
+
- Bump local dev version, rubocop fixes, add backstage catalog file + sonarqube project settings
|
20
|
+
|
21
|
+
## 4.0.1 - 2020-03-23
|
22
|
+
### Fixes
|
23
|
+
- Fixes 4.0.0. Instead of expecting message attributes in the message from `poll`, retrieves them from the right place.
|
24
|
+
|
25
|
+
## 4.0.0 - 2020-03-18
|
26
|
+
### Breaking Changes
|
27
|
+
- Add the ability for SQS to receive message attributes
|
28
|
+
- `handle` function of `QueuePoller` now takes a third parameter `message_attributes`
|
29
|
+
- blocks or `MessageHandler`s passed to `QueuePoller` can use this `message_attributes`
|
30
|
+
|
7
31
|
## 3.4.0 - 2020-03-17
|
8
32
|
### Added
|
9
33
|
- Add the ability to pass message attributes to SNS
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
pheme (4.0.5)
|
5
|
+
activesupport (>= 4)
|
6
|
+
aws-sdk-sns (~> 1.1)
|
7
|
+
aws-sdk-sqs (~> 1.3)
|
8
|
+
recursive-open-struct (~> 1)
|
9
|
+
smarter_csv (~> 1)
|
10
|
+
|
11
|
+
GEM
|
12
|
+
remote: https://rubygems.org/
|
13
|
+
specs:
|
14
|
+
activesupport (6.1.3.1)
|
15
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
16
|
+
i18n (>= 1.6, < 2)
|
17
|
+
minitest (>= 5.1)
|
18
|
+
tzinfo (~> 2.0)
|
19
|
+
zeitwerk (~> 2.3)
|
20
|
+
ast (2.4.2)
|
21
|
+
aws-eventstream (1.1.1)
|
22
|
+
aws-partitions (1.446.0)
|
23
|
+
aws-sdk-core (3.114.0)
|
24
|
+
aws-eventstream (~> 1, >= 1.0.2)
|
25
|
+
aws-partitions (~> 1, >= 1.239.0)
|
26
|
+
aws-sigv4 (~> 1.1)
|
27
|
+
jmespath (~> 1.0)
|
28
|
+
aws-sdk-sns (1.39.0)
|
29
|
+
aws-sdk-core (~> 3, >= 3.112.0)
|
30
|
+
aws-sigv4 (~> 1.1)
|
31
|
+
aws-sdk-sqs (1.38.0)
|
32
|
+
aws-sdk-core (~> 3, >= 3.112.0)
|
33
|
+
aws-sigv4 (~> 1.1)
|
34
|
+
aws-sigv4 (1.2.3)
|
35
|
+
aws-eventstream (~> 1, >= 1.0.2)
|
36
|
+
bundler-audit (0.8.0)
|
37
|
+
bundler (>= 1.2.0, < 3)
|
38
|
+
thor (~> 1.0)
|
39
|
+
concurrent-ruby (1.1.8)
|
40
|
+
diff-lcs (1.4.4)
|
41
|
+
docile (1.3.5)
|
42
|
+
git (1.8.1)
|
43
|
+
rchardet (~> 1.8)
|
44
|
+
i18n (1.8.10)
|
45
|
+
concurrent-ruby (~> 1.0)
|
46
|
+
jmespath (1.4.0)
|
47
|
+
json (2.5.1)
|
48
|
+
minitest (5.14.4)
|
49
|
+
parallel (1.20.1)
|
50
|
+
parser (3.0.1.0)
|
51
|
+
ast (~> 2.4.1)
|
52
|
+
rack (2.2.3)
|
53
|
+
rainbow (3.0.0)
|
54
|
+
rake (13.0.3)
|
55
|
+
rchardet (1.8.0)
|
56
|
+
recursive-open-struct (1.1.3)
|
57
|
+
regexp_parser (2.1.1)
|
58
|
+
rexml (3.2.5)
|
59
|
+
rspec (3.10.0)
|
60
|
+
rspec-core (~> 3.10.0)
|
61
|
+
rspec-expectations (~> 3.10.0)
|
62
|
+
rspec-mocks (~> 3.10.0)
|
63
|
+
rspec-collection_matchers (1.2.0)
|
64
|
+
rspec-expectations (>= 2.99.0.beta1)
|
65
|
+
rspec-core (3.10.1)
|
66
|
+
rspec-support (~> 3.10.0)
|
67
|
+
rspec-expectations (3.10.1)
|
68
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
69
|
+
rspec-support (~> 3.10.0)
|
70
|
+
rspec-its (1.3.0)
|
71
|
+
rspec-core (>= 3.0.0)
|
72
|
+
rspec-expectations (>= 3.0.0)
|
73
|
+
rspec-mocks (3.10.2)
|
74
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
75
|
+
rspec-support (~> 3.10.0)
|
76
|
+
rspec-support (3.10.2)
|
77
|
+
rspec_junit_formatter (0.4.1)
|
78
|
+
rspec-core (>= 2, < 4, != 2.12.0)
|
79
|
+
rubocop (1.12.1)
|
80
|
+
parallel (~> 1.10)
|
81
|
+
parser (>= 3.0.0.0)
|
82
|
+
rainbow (>= 2.2.2, < 4.0)
|
83
|
+
regexp_parser (>= 1.8, < 3.0)
|
84
|
+
rexml
|
85
|
+
rubocop-ast (>= 1.2.0, < 2.0)
|
86
|
+
ruby-progressbar (~> 1.7)
|
87
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
88
|
+
rubocop-ast (1.4.1)
|
89
|
+
parser (>= 2.7.1.5)
|
90
|
+
rubocop-performance (1.10.2)
|
91
|
+
rubocop (>= 0.90.0, < 2.0)
|
92
|
+
rubocop-ast (>= 0.4.0)
|
93
|
+
rubocop-rails (2.9.1)
|
94
|
+
activesupport (>= 4.2.0)
|
95
|
+
rack (>= 1.1)
|
96
|
+
rubocop (>= 0.90.0, < 2.0)
|
97
|
+
rubocop-rspec (2.2.0)
|
98
|
+
rubocop (~> 1.0)
|
99
|
+
rubocop-ast (>= 1.1.0)
|
100
|
+
rubocop-vendor (0.6.0)
|
101
|
+
rubocop (>= 0.53.0)
|
102
|
+
ruby-progressbar (1.11.0)
|
103
|
+
simplecov (0.16.1)
|
104
|
+
docile (~> 1.1)
|
105
|
+
json (>= 1.8, < 3)
|
106
|
+
simplecov-html (~> 0.10.0)
|
107
|
+
simplecov-html (0.10.2)
|
108
|
+
smarter_csv (1.2.8)
|
109
|
+
thor (1.1.0)
|
110
|
+
tzinfo (2.0.4)
|
111
|
+
concurrent-ruby (~> 1.0)
|
112
|
+
unicode-display_width (2.0.0)
|
113
|
+
ws-style (6.9.0)
|
114
|
+
rubocop (>= 1.12.1)
|
115
|
+
rubocop-performance (>= 1.10.2)
|
116
|
+
rubocop-rails (>= 2.9.1)
|
117
|
+
rubocop-rspec (>= 2.2.0)
|
118
|
+
rubocop-vendor (>= 0.6.0)
|
119
|
+
zeitwerk (2.4.2)
|
120
|
+
|
121
|
+
PLATFORMS
|
122
|
+
ruby
|
123
|
+
|
124
|
+
DEPENDENCIES
|
125
|
+
bundler
|
126
|
+
bundler-audit
|
127
|
+
git
|
128
|
+
pheme!
|
129
|
+
rake
|
130
|
+
rspec
|
131
|
+
rspec-collection_matchers
|
132
|
+
rspec-its
|
133
|
+
rspec_junit_formatter (~> 0.2)
|
134
|
+
simplecov
|
135
|
+
ws-style
|
136
|
+
|
137
|
+
BUNDLED WITH
|
138
|
+
2.2.16
|
data/README.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
# pheme
|
1
|
+
# pheme
|
2
|
+
[](https://github.com/wealthsimple/pheme/actions)
|
3
|
+
|
4
|
+
[](https://github.com/wealthsimple/pheme/actions)
|
2
5
|
|
3
6
|
Ruby SNS publisher + SQS poller & message handler
|
4
7
|
|
@@ -39,3 +42,9 @@ end
|
|
39
42
|
See https://github.com/wealthsimple/pheme/tree/master/spec/support for example implementations of each class.
|
40
43
|
|
41
44
|
TODO: write better usage instructions.
|
45
|
+
|
46
|
+
### Handling SQS messages
|
47
|
+
|
48
|
+
Pheme expects that the SQS messages it is handling will have first been published to an SNS topic
|
49
|
+
before being sent to the SQS queue. This means if the service publishing messages is publishing them
|
50
|
+
**directly** to the SQS queue, that service must nest the message payload underneath a `Message` property.
|
data/catalog-info.yaml
ADDED
data/lib/pheme/configuration.rb
CHANGED
@@ -19,8 +19,7 @@ module Pheme
|
|
19
19
|
ATTRIBUTES = %i[sns_client sqs_client logger].freeze
|
20
20
|
OPTIONAL_ATTRIBUTES = %i[rollbar].freeze
|
21
21
|
|
22
|
-
attr_accessor(*ATTRIBUTES)
|
23
|
-
attr_accessor(*OPTIONAL_ATTRIBUTES)
|
22
|
+
attr_accessor(*ATTRIBUTES, *OPTIONAL_ATTRIBUTES)
|
24
23
|
|
25
24
|
def initialize
|
26
25
|
@logger ||= Logger.new(STDOUT) # rubocop:disable Lint/DisjunctiveAssignmentInConstructor
|
@@ -1,10 +1,11 @@
|
|
1
1
|
module Pheme
|
2
2
|
class MessageHandler
|
3
|
-
attr_reader :message, :metadata, :timestamp
|
3
|
+
attr_reader :message, :metadata, :message_attributes, :timestamp
|
4
4
|
|
5
|
-
def initialize(message:, metadata: {})
|
5
|
+
def initialize(message:, metadata: {}, message_attributes: {})
|
6
6
|
@message = message
|
7
7
|
@metadata = metadata
|
8
|
+
@message_attributes = message_attributes
|
8
9
|
end
|
9
10
|
|
10
11
|
def handle
|
data/lib/pheme/queue_poller.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require_relative 'compression'
|
2
2
|
|
3
3
|
module Pheme
|
4
|
+
# rubocop:disable Metrics/ClassLength
|
4
5
|
class QueuePoller
|
5
6
|
include Compression
|
6
7
|
|
@@ -51,28 +52,29 @@ module Pheme
|
|
51
52
|
end
|
52
53
|
end
|
53
54
|
|
55
|
+
# rubocop:disable Metrics/AbcSize
|
54
56
|
def poll
|
55
57
|
time_start = log_polling_start
|
56
58
|
queue_poller.poll(poller_configuration) do |queue_message|
|
57
59
|
@messages_received += 1
|
58
60
|
Pheme.logger.tagged(queue_message.message_id) do
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
61
|
+
content = parse_body(queue_message)
|
62
|
+
metadata = parse_metadata(queue_message)
|
63
|
+
message_attributes = parse_message_attributes(queue_message)
|
64
|
+
with_optional_connection_pool_block { handle(content, metadata, message_attributes) }
|
65
|
+
queue_poller.delete_message(queue_message)
|
66
|
+
log_delete(queue_message)
|
67
|
+
@messages_processed += 1
|
68
|
+
rescue SignalException
|
69
|
+
throw :stop_polling
|
70
|
+
rescue StandardError => e
|
71
|
+
Pheme.logger.error(e)
|
72
|
+
Pheme.rollbar(e, "#{self.class} failed to process message", { message: content })
|
72
73
|
end
|
73
74
|
end
|
74
75
|
log_polling_end(time_start)
|
75
76
|
end
|
77
|
+
# rubocop:enable Metrics/AbcSize
|
76
78
|
|
77
79
|
# returns queue_message.body as hash,
|
78
80
|
# stores and parses get_content to body[:content]
|
@@ -105,6 +107,16 @@ module Pheme
|
|
105
107
|
{ timestamp: message_body['Timestamp'], topic_arn: message_body['TopicArn'] }
|
106
108
|
end
|
107
109
|
|
110
|
+
def parse_message_attributes(queue_message)
|
111
|
+
message_body = JSON.parse(queue_message.body)
|
112
|
+
message_attributes = {}
|
113
|
+
message_body['MessageAttributes']&.each do |key, value|
|
114
|
+
message_attributes[key.to_sym] = coerce_message_attribute(value)
|
115
|
+
end
|
116
|
+
|
117
|
+
message_attributes
|
118
|
+
end
|
119
|
+
|
108
120
|
def get_metadata(message_body)
|
109
121
|
message_body.except('Message', 'Records')
|
110
122
|
end
|
@@ -123,11 +135,11 @@ module Pheme
|
|
123
135
|
RecursiveOpenStruct.new({ wrapper: parsed_body }, recurse_over_arrays: true).wrapper
|
124
136
|
end
|
125
137
|
|
126
|
-
def handle(message, metadata)
|
138
|
+
def handle(message, metadata, message_attributes)
|
127
139
|
if @message_handler
|
128
|
-
@message_handler.new(message: message, metadata: metadata).handle
|
140
|
+
@message_handler.new(message: message, metadata: metadata, message_attributes: message_attributes).handle
|
129
141
|
elsif @block_message_handler
|
130
|
-
@block_message_handler.call(message, metadata)
|
142
|
+
@block_message_handler.call(message, metadata, message_attributes)
|
131
143
|
else
|
132
144
|
raise NotImplementedError
|
133
145
|
end
|
@@ -135,9 +147,21 @@ module Pheme
|
|
135
147
|
|
136
148
|
private
|
137
149
|
|
138
|
-
def
|
150
|
+
def coerce_message_attribute(value)
|
151
|
+
case value['Type']
|
152
|
+
when 'Binary', 'String'
|
153
|
+
value['Value']
|
154
|
+
when 'Number', 'String.Array'
|
155
|
+
JSON.parse(value['Value'])
|
156
|
+
else
|
157
|
+
Pheme.logger.info("Unsupported custom data type")
|
158
|
+
value["Value"]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def with_optional_connection_pool_block(&block)
|
139
163
|
if connection_pool_block
|
140
|
-
ActiveRecord::Base.connection_pool.with_connection
|
164
|
+
ActiveRecord::Base.connection_pool.with_connection(&block)
|
141
165
|
else
|
142
166
|
yield
|
143
167
|
end
|
@@ -192,4 +216,5 @@ module Pheme
|
|
192
216
|
}.to_json)
|
193
217
|
end
|
194
218
|
end
|
219
|
+
# rubocop:enable Metrics/ClassLength
|
195
220
|
end
|
data/lib/pheme/version.rb
CHANGED
data/pheme.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.require_paths = ["lib"]
|
21
21
|
s.license = "MIT"
|
22
22
|
|
23
|
-
s.required_ruby_version = '>= 2.
|
23
|
+
s.required_ruby_version = '>= 2.7.3'
|
24
24
|
|
25
25
|
s.add_dependency "activesupport", ">= 4"
|
26
26
|
s.add_dependency "aws-sdk-sns", "~> 1.1"
|
@@ -29,7 +29,6 @@ Gem::Specification.new do |s|
|
|
29
29
|
s.add_dependency "smarter_csv", "~> 1"
|
30
30
|
|
31
31
|
s.add_development_dependency 'bundler'
|
32
|
-
s.add_development_dependency 'coveralls'
|
33
32
|
s.add_development_dependency 'git'
|
34
33
|
s.add_development_dependency 'rake'
|
35
34
|
s.add_development_dependency 'rspec'
|
data/spec/queue_poller_spec.rb
CHANGED
@@ -14,7 +14,7 @@ describe Pheme::QueuePoller do
|
|
14
14
|
let(:message_id) { SecureRandom.uuid }
|
15
15
|
|
16
16
|
context 'base poller' do
|
17
|
-
subject { described_class.new(queue_url: queue_url).handle(nil, nil) }
|
17
|
+
subject { described_class.new(queue_url: queue_url).handle(nil, nil, nil) }
|
18
18
|
|
19
19
|
it 'does not implement handle' do
|
20
20
|
expect { subject }.to raise_error(NotImplementedError)
|
@@ -90,13 +90,13 @@ describe Pheme::QueuePoller do
|
|
90
90
|
context 'when handling messages' do
|
91
91
|
context 'when doing it the old way, via the handle function' do
|
92
92
|
it 'uses the handle function by default' do
|
93
|
-
expect { described_class.new(queue_url: queue_url).handle(nil, nil) }.to raise_error(NotImplementedError)
|
93
|
+
expect { described_class.new(queue_url: queue_url).handle(nil, nil, nil) }.to raise_error(NotImplementedError)
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
97
97
|
context 'when given a message_handler as parameter' do
|
98
98
|
it 'uses default when given nil' do
|
99
|
-
expect { described_class.new(queue_url: queue_url, message_handler: nil).handle(nil, nil) }.to raise_error(NotImplementedError)
|
99
|
+
expect { described_class.new(queue_url: queue_url, message_handler: nil).handle(nil, nil, nil) }.to raise_error(NotImplementedError)
|
100
100
|
end
|
101
101
|
|
102
102
|
it 'uses default when given invalid message_handler' do
|
@@ -106,9 +106,11 @@ describe Pheme::QueuePoller do
|
|
106
106
|
it 'uses handler when given one' do
|
107
107
|
mock_handler = double('MessageHandler')
|
108
108
|
allow(mock_handler).to receive(:handle)
|
109
|
-
allow(ExampleMessageHandler).to receive(:new).
|
109
|
+
allow(ExampleMessageHandler).to receive(:new).
|
110
|
+
with(message: 'message', metadata: 'metadata', message_attributes: 'message_attributes').
|
111
|
+
and_return(mock_handler)
|
110
112
|
|
111
|
-
described_class.new(queue_url: queue_url, message_handler: ExampleMessageHandler).handle('message', 'metadata')
|
113
|
+
described_class.new(queue_url: queue_url, message_handler: ExampleMessageHandler).handle('message', 'metadata', 'message_attributes')
|
112
114
|
expect(mock_handler).to have_received(:handle).once
|
113
115
|
end
|
114
116
|
end
|
@@ -117,12 +119,12 @@ describe Pheme::QueuePoller do
|
|
117
119
|
it 'uses handler when given one' do
|
118
120
|
mock_handler = spy('MessageHandler')
|
119
121
|
|
120
|
-
poller = described_class.new(queue_url: queue_url) do |message, metadata|
|
121
|
-
mock_handler.process(message, metadata)
|
122
|
+
poller = described_class.new(queue_url: queue_url) do |message, metadata, message_attributes|
|
123
|
+
mock_handler.process(message, metadata, message_attributes)
|
122
124
|
end
|
123
|
-
poller.handle('message', 'metadata')
|
125
|
+
poller.handle('message', 'metadata', 'message_attributes')
|
124
126
|
|
125
|
-
expect(mock_handler).to have_received(:process).with('message', 'metadata').once
|
127
|
+
expect(mock_handler).to have_received(:process).with('message', 'metadata', 'message_attributes').once
|
126
128
|
end
|
127
129
|
|
128
130
|
it 'fails on invalid handler' do
|
@@ -134,6 +136,62 @@ describe Pheme::QueuePoller do
|
|
134
136
|
end
|
135
137
|
end
|
136
138
|
|
139
|
+
describe '#parse_message_attributes' do
|
140
|
+
subject { described_class.new(queue_url: queue_url).parse_message_attributes(queue_message) }
|
141
|
+
|
142
|
+
context 'when no message_attributes' do
|
143
|
+
it { is_expected.to eq({}) }
|
144
|
+
end
|
145
|
+
|
146
|
+
context 'when message attributes' do
|
147
|
+
let(:queue_message) do
|
148
|
+
OpenStruct.new(
|
149
|
+
body: {
|
150
|
+
Message: message,
|
151
|
+
MessageAttributes: {
|
152
|
+
key: { Type: data_type, Value: string_value },
|
153
|
+
},
|
154
|
+
}.to_json,
|
155
|
+
message_id: message_id,
|
156
|
+
)
|
157
|
+
end
|
158
|
+
|
159
|
+
context 'when data_type is String' do
|
160
|
+
let(:data_type) { 'String' }
|
161
|
+
let(:string_value) { 'some-string-value' }
|
162
|
+
let(:expected_value) { 'some-string-value' }
|
163
|
+
|
164
|
+
its([:key]) { is_expected.to eq(expected_value) }
|
165
|
+
end
|
166
|
+
|
167
|
+
context 'when data_type is Number' do
|
168
|
+
let(:data_type) { 'Number' }
|
169
|
+
|
170
|
+
context 'and it is an int' do
|
171
|
+
let(:string_value) { '1' }
|
172
|
+
let(:expected_value) { 1 }
|
173
|
+
|
174
|
+
its([:key]) { is_expected.to eq(expected_value) }
|
175
|
+
end
|
176
|
+
|
177
|
+
context 'and it is a float' do
|
178
|
+
let(:string_value) { '1.5' }
|
179
|
+
let(:expected_value) { 1.5 }
|
180
|
+
|
181
|
+
its([:key]) { is_expected.to eq(expected_value) }
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
context 'when data_type is String.array' do
|
186
|
+
let(:data_type) { 'String.Array' }
|
187
|
+
let(:string_value) { '["sdfsdf",1,2,null,true,false]' }
|
188
|
+
let(:expected_value) { ["sdfsdf", 1, 2, nil, true, false] }
|
189
|
+
|
190
|
+
its([:key]) { is_expected.to eq(expected_value) }
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
137
195
|
describe "#parse_body" do
|
138
196
|
subject { poller.parse_body(queue_message) }
|
139
197
|
|
@@ -305,6 +363,7 @@ describe Pheme::QueuePoller do
|
|
305
363
|
expect(ExampleMessageHandler).to receive(:new).with(
|
306
364
|
message: RecursiveOpenStruct.new(message),
|
307
365
|
metadata: { timestamp: timestamp, topic_arn: topic_arn },
|
366
|
+
message_attributes: {},
|
308
367
|
)
|
309
368
|
subject.poll
|
310
369
|
end
|
@@ -359,7 +418,7 @@ describe Pheme::QueuePoller do
|
|
359
418
|
|
360
419
|
let(:queue_message) { OpenStruct.new(body: { 'Records' => records }.to_json) }
|
361
420
|
let(:records) do
|
362
|
-
[{ 'eventVersion' => '2.0',
|
421
|
+
[{ 'eventVersion' => '2.0', eventSource: 'aws:s3' }]
|
363
422
|
end
|
364
423
|
|
365
424
|
before do
|
data/spec/spec_helper.rb
CHANGED
@@ -3,10 +3,10 @@ class ExampleQueuePoller < Pheme::QueuePoller
|
|
3
3
|
super(queue_url: queue_url, **kwargs)
|
4
4
|
end
|
5
5
|
|
6
|
-
def handle(message, metadata)
|
6
|
+
def handle(message, metadata, message_attributes)
|
7
7
|
case message.status
|
8
8
|
when 'complete', 'rejected'
|
9
|
-
ExampleMessageHandler.new(message: message, metadata: metadata).handle
|
9
|
+
ExampleMessageHandler.new(message: message, metadata: metadata, message_attributes: message_attributes).handle
|
10
10
|
else
|
11
11
|
raise ArgumentError, "Unknown message status: #{message.status}"
|
12
12
|
end
|
data/spec/version_spec.rb
CHANGED
@@ -4,7 +4,7 @@ describe Pheme do
|
|
4
4
|
def get_version(git, branch = 'HEAD')
|
5
5
|
git.grep('VERSION = ', 'lib/*/version.rb', { object: branch }).
|
6
6
|
map { |_sha, matches| matches.first[1] }.
|
7
|
-
map(
|
7
|
+
map { |version_string| parse_version(version_string) }.
|
8
8
|
reject(&:nil?).
|
9
9
|
first
|
10
10
|
end
|
@@ -21,14 +21,12 @@ describe Pheme do
|
|
21
21
|
|
22
22
|
it 'has a bumped version' do
|
23
23
|
git = Git.open('.')
|
24
|
-
|
24
|
+
main_version = get_version(git, 'origin/main')
|
25
|
+
skip('first time publishing, no need to compare versions') if main_version.nil?
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
raise 'no version.rb file found on the current branch' if head_version.nil?
|
30
|
-
raise 'no version.rb file found on the master branch' if master_version.nil?
|
27
|
+
is_main_branch = git.current_branch == 'main'
|
28
|
+
skip('already on main branch, no need to compare versions') if is_main_branch
|
31
29
|
|
32
|
-
expect(Gem::Version.new(head_version)).to be > Gem::Version.new(
|
30
|
+
expect(Gem::Version.new(head_version)).to be > Gem::Version.new(main_version)
|
33
31
|
end
|
34
32
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pheme
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Graham
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-04-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -94,20 +94,6 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: coveralls
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - ">="
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
111
97
|
- !ruby/object:Gem::Dependency
|
112
98
|
name: git
|
113
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -227,9 +213,9 @@ executables: []
|
|
227
213
|
extensions: []
|
228
214
|
extra_rdoc_files: []
|
229
215
|
files:
|
230
|
-
- ".circleci/config.yml"
|
231
216
|
- ".github/CODEOWNERS"
|
232
217
|
- ".github/PULL_REQUEST_TEMPLATE.md"
|
218
|
+
- ".github/workflows/main.yml"
|
233
219
|
- ".gitignore"
|
234
220
|
- ".rspec"
|
235
221
|
- ".rubocop.yml"
|
@@ -237,9 +223,11 @@ files:
|
|
237
223
|
- ".ruby-version"
|
238
224
|
- CHANGELOG.md
|
239
225
|
- Gemfile
|
226
|
+
- Gemfile.lock
|
240
227
|
- LICENSE
|
241
228
|
- README.md
|
242
229
|
- Rakefile
|
230
|
+
- catalog-info.yaml
|
243
231
|
- lib/pheme.rb
|
244
232
|
- lib/pheme/compression.rb
|
245
233
|
- lib/pheme/configuration.rb
|
@@ -252,6 +240,7 @@ files:
|
|
252
240
|
- lib/pheme/topic_publisher.rb
|
253
241
|
- lib/pheme/version.rb
|
254
242
|
- pheme.gemspec
|
243
|
+
- sonar-project.properties
|
255
244
|
- spec/configuration_spec.rb
|
256
245
|
- spec/logger_spec.rb
|
257
246
|
- spec/message_handler_spec.rb
|
@@ -281,15 +270,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
281
270
|
requirements:
|
282
271
|
- - ">="
|
283
272
|
- !ruby/object:Gem::Version
|
284
|
-
version:
|
273
|
+
version: 2.7.3
|
285
274
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
286
275
|
requirements:
|
287
276
|
- - ">="
|
288
277
|
- !ruby/object:Gem::Version
|
289
278
|
version: '0'
|
290
279
|
requirements: []
|
291
|
-
|
292
|
-
rubygems_version: 2.7.6
|
280
|
+
rubygems_version: 3.1.6
|
293
281
|
signing_key:
|
294
282
|
specification_version: 4
|
295
283
|
summary: Ruby SNS publisher + SQS poller & message handler
|
data/.circleci/config.yml
DELETED
@@ -1,149 +0,0 @@
|
|
1
|
-
version: 2
|
2
|
-
|
3
|
-
references:
|
4
|
-
defaults:
|
5
|
-
2_4_5: &defaults_2_4_5
|
6
|
-
working_directory: ~/wealthsimple
|
7
|
-
docker:
|
8
|
-
- image: circleci/ruby:2.4.5
|
9
|
-
environment:
|
10
|
-
RAILS_ENV: test
|
11
|
-
|
12
|
-
2_5_3: &defaults
|
13
|
-
working_directory: ~/wealthsimple
|
14
|
-
docker:
|
15
|
-
- image: circleci/ruby:2.5.3
|
16
|
-
environment:
|
17
|
-
RAILS_ENV: test
|
18
|
-
|
19
|
-
which_bundler: &which_bundler
|
20
|
-
run:
|
21
|
-
name: Which bundler?
|
22
|
-
command: bundle -v
|
23
|
-
|
24
|
-
bundle_install: &bundle_install
|
25
|
-
run:
|
26
|
-
name: Bundle Install
|
27
|
-
command: bundle install --jobs=4 --retry=3 --path vendor/bundle
|
28
|
-
|
29
|
-
attach_code_workspace: &attach_code_workspace
|
30
|
-
attach_workspace:
|
31
|
-
at: ~/wealthsimple
|
32
|
-
|
33
|
-
restore_bundle_dependencies: &restore_bundle_dependencies
|
34
|
-
run:
|
35
|
-
name: Restore bundle dependencies from workspace
|
36
|
-
command: bundle --path vendor/bundle
|
37
|
-
|
38
|
-
run_rspec: &run_rspec
|
39
|
-
run:
|
40
|
-
name: Run rspec
|
41
|
-
command: |
|
42
|
-
bundle exec rspec \
|
43
|
-
--require spec_helper \
|
44
|
-
--format RspecJunitFormatter \
|
45
|
-
--out test_results/rspec.xml \
|
46
|
-
--format documentation
|
47
|
-
|
48
|
-
jobs:
|
49
|
-
checkout_and_bundle_2_4_5:
|
50
|
-
<<: *defaults_2_4_5
|
51
|
-
environment:
|
52
|
-
COVERALLS_REPO_TOKEN: ''
|
53
|
-
steps:
|
54
|
-
- checkout
|
55
|
-
- *which_bundler
|
56
|
-
- *bundle_install
|
57
|
-
- *run_rspec
|
58
|
-
- store_test_results:
|
59
|
-
path: test_results
|
60
|
-
|
61
|
-
checkout_and_bundle:
|
62
|
-
<<: *defaults
|
63
|
-
steps:
|
64
|
-
- checkout
|
65
|
-
- *which_bundler
|
66
|
-
- *bundle_install
|
67
|
-
- persist_to_workspace:
|
68
|
-
root: .
|
69
|
-
paths: .
|
70
|
-
|
71
|
-
rspec:
|
72
|
-
<<: *defaults
|
73
|
-
steps:
|
74
|
-
- *attach_code_workspace
|
75
|
-
- *restore_bundle_dependencies
|
76
|
-
- *run_rspec
|
77
|
-
- store_test_results:
|
78
|
-
path: test_results
|
79
|
-
|
80
|
-
lint_check:
|
81
|
-
<<: *defaults
|
82
|
-
steps:
|
83
|
-
- *attach_code_workspace
|
84
|
-
- *restore_bundle_dependencies
|
85
|
-
- run: bundle exec rubocop
|
86
|
-
|
87
|
-
vulnerability_check:
|
88
|
-
<<: *defaults
|
89
|
-
steps:
|
90
|
-
- *attach_code_workspace
|
91
|
-
- *restore_bundle_dependencies
|
92
|
-
- run: bundle exec bundle-audit update && bundle exec bundle-audit check
|
93
|
-
|
94
|
-
release:
|
95
|
-
<<: *defaults
|
96
|
-
steps:
|
97
|
-
- checkout
|
98
|
-
- *attach_code_workspace
|
99
|
-
- *restore_bundle_dependencies
|
100
|
-
- run:
|
101
|
-
name: Release to rubygems.org
|
102
|
-
command: |
|
103
|
-
mkdir ~/.gem
|
104
|
-
echo ":rubygems_api_key: ${RUBYGEMS_API_KEY}" >> ~/.gem/credentials
|
105
|
-
chmod 600 ~/.gem/credentials
|
106
|
-
bundle exec rake release
|
107
|
-
|
108
|
-
workflows:
|
109
|
-
version: 2
|
110
|
-
build:
|
111
|
-
jobs:
|
112
|
-
- checkout_and_bundle_2_4_5:
|
113
|
-
context: wealthsimple
|
114
|
-
- checkout_and_bundle:
|
115
|
-
context: wealthsimple
|
116
|
-
- rspec:
|
117
|
-
requires:
|
118
|
-
- checkout_and_bundle
|
119
|
-
- lint_check:
|
120
|
-
requires:
|
121
|
-
- checkout_and_bundle
|
122
|
-
- vulnerability_check:
|
123
|
-
requires:
|
124
|
-
- checkout_and_bundle
|
125
|
-
- release:
|
126
|
-
context: wealthsimple
|
127
|
-
filters:
|
128
|
-
branches:
|
129
|
-
only: master
|
130
|
-
requires:
|
131
|
-
- checkout_and_bundle_2_4_5
|
132
|
-
- rspec
|
133
|
-
- lint_check
|
134
|
-
- vulnerability_check
|
135
|
-
|
136
|
-
security-audit:
|
137
|
-
triggers:
|
138
|
-
- schedule:
|
139
|
-
# 11:05 am UTC: 6:05 am EST / 7:05 am EDT
|
140
|
-
cron: "5 11 * * *"
|
141
|
-
filters:
|
142
|
-
branches:
|
143
|
-
only: master
|
144
|
-
jobs:
|
145
|
-
- checkout_and_bundle:
|
146
|
-
context: wealthsimple
|
147
|
-
- vulnerability_check:
|
148
|
-
requires:
|
149
|
-
- checkout_and_bundle
|