cloudenvoy 0.1.0.dev → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +41 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +1 -0
- data/Appraisals +25 -0
- data/CHANGELOG.md +32 -0
- data/Gemfile.lock +215 -1
- data/README.md +581 -7
- data/app/controllers/cloudenvoy/application_controller.rb +8 -0
- data/app/controllers/cloudenvoy/subscriber_controller.rb +59 -0
- data/cloudenvoy.gemspec +15 -2
- data/config/routes.rb +5 -0
- data/examples/rails/.ruby-version +1 -0
- data/examples/rails/Gemfile +15 -0
- data/examples/rails/Gemfile.lock +207 -0
- data/examples/rails/Procfile +1 -0
- data/examples/rails/README.md +31 -0
- data/examples/rails/Rakefile +8 -0
- data/examples/rails/app/assets/config/manifest.js +2 -0
- data/examples/rails/app/assets/images/.keep +0 -0
- data/examples/rails/app/assets/stylesheets/application.css +15 -0
- data/examples/rails/app/channels/application_cable/channel.rb +6 -0
- data/examples/rails/app/channels/application_cable/connection.rb +6 -0
- data/examples/rails/app/controllers/application_controller.rb +4 -0
- data/examples/rails/app/controllers/concerns/.keep +0 -0
- data/examples/rails/app/helpers/application_helper.rb +4 -0
- data/examples/rails/app/javascript/packs/application.js +15 -0
- data/examples/rails/app/jobs/application_job.rb +9 -0
- data/examples/rails/app/mailers/application_mailer.rb +6 -0
- data/examples/rails/app/models/application_record.rb +5 -0
- data/examples/rails/app/models/concerns/.keep +0 -0
- data/examples/rails/app/publishers/hello_publisher.rb +34 -0
- data/examples/rails/app/subscribers/hello_subscriber.rb +16 -0
- data/examples/rails/app/views/layouts/application.html.erb +14 -0
- data/examples/rails/app/views/layouts/mailer.html.erb +13 -0
- data/examples/rails/app/views/layouts/mailer.text.erb +1 -0
- data/examples/rails/bin/rails +6 -0
- data/examples/rails/bin/rake +6 -0
- data/examples/rails/bin/setup +35 -0
- data/examples/rails/config.ru +7 -0
- data/examples/rails/config/application.rb +19 -0
- data/examples/rails/config/boot.rb +7 -0
- data/examples/rails/config/cable.yml +10 -0
- data/examples/rails/config/credentials.yml.enc +1 -0
- data/examples/rails/config/database.yml +25 -0
- data/examples/rails/config/environment.rb +7 -0
- data/examples/rails/config/environments/development.rb +65 -0
- data/examples/rails/config/environments/production.rb +114 -0
- data/examples/rails/config/environments/test.rb +50 -0
- data/examples/rails/config/initializers/application_controller_renderer.rb +9 -0
- data/examples/rails/config/initializers/assets.rb +14 -0
- data/examples/rails/config/initializers/backtrace_silencers.rb +8 -0
- data/examples/rails/config/initializers/cloudenvoy.rb +22 -0
- data/examples/rails/config/initializers/content_security_policy.rb +29 -0
- data/examples/rails/config/initializers/cookies_serializer.rb +7 -0
- data/examples/rails/config/initializers/filter_parameter_logging.rb +6 -0
- data/examples/rails/config/initializers/inflections.rb +17 -0
- data/examples/rails/config/initializers/mime_types.rb +5 -0
- data/examples/rails/config/initializers/wrap_parameters.rb +16 -0
- data/examples/rails/config/locales/en.yml +33 -0
- data/examples/rails/config/master.key +1 -0
- data/examples/rails/config/puma.rb +37 -0
- data/examples/rails/config/routes.rb +4 -0
- data/examples/rails/config/spring.rb +8 -0
- data/examples/rails/config/storage.yml +34 -0
- data/examples/rails/db/development.sqlite3 +0 -0
- data/examples/rails/db/test.sqlite3 +0 -0
- data/examples/rails/lib/assets/.keep +0 -0
- data/examples/rails/log/.keep +0 -0
- data/examples/rails/public/404.html +67 -0
- data/examples/rails/public/422.html +67 -0
- data/examples/rails/public/500.html +66 -0
- data/examples/rails/public/apple-touch-icon-precomposed.png +0 -0
- data/examples/rails/public/apple-touch-icon.png +0 -0
- data/examples/rails/public/favicon.ico +0 -0
- data/examples/rails/storage/.keep +0 -0
- data/gemfiles/rails_5.2.gemfile +7 -0
- data/gemfiles/rails_5.2.gemfile.lock +251 -0
- data/gemfiles/rails_6.0.gemfile +7 -0
- data/gemfiles/rails_6.0.gemfile.lock +267 -0
- data/gemfiles/semantic_logger_3.4.gemfile +7 -0
- data/gemfiles/semantic_logger_3.4.gemfile.lock +265 -0
- data/gemfiles/semantic_logger_4.6.gemfile +7 -0
- data/gemfiles/semantic_logger_4.6.gemfile.lock +265 -0
- data/gemfiles/semantic_logger_4.7.0.gemfile +7 -0
- data/gemfiles/semantic_logger_4.7.0.gemfile.lock +265 -0
- data/gemfiles/semantic_logger_4.7.2.gemfile +7 -0
- data/gemfiles/semantic_logger_4.7.2.gemfile.lock +265 -0
- data/lib/cloudenvoy.rb +96 -2
- data/lib/cloudenvoy/authentication_error.rb +6 -0
- data/lib/cloudenvoy/authenticator.rb +57 -0
- data/lib/cloudenvoy/backend/google_pub_sub.rb +146 -0
- data/lib/cloudenvoy/backend/memory_pub_sub.rb +89 -0
- data/lib/cloudenvoy/config.rb +165 -0
- data/lib/cloudenvoy/engine.rb +20 -0
- data/lib/cloudenvoy/invalid_subscriber_error.rb +6 -0
- data/lib/cloudenvoy/logger_wrapper.rb +167 -0
- data/lib/cloudenvoy/message.rb +96 -0
- data/lib/cloudenvoy/middleware/chain.rb +250 -0
- data/lib/cloudenvoy/pub_sub_client.rb +76 -0
- data/lib/cloudenvoy/publisher.rb +211 -0
- data/lib/cloudenvoy/publisher_logger.rb +32 -0
- data/lib/cloudenvoy/subscriber.rb +222 -0
- data/lib/cloudenvoy/subscriber_logger.rb +26 -0
- data/lib/cloudenvoy/subscription.rb +19 -0
- data/lib/cloudenvoy/testing.rb +106 -0
- data/lib/cloudenvoy/topic.rb +19 -0
- data/lib/cloudenvoy/version.rb +1 -1
- data/lib/tasks/cloudenvoy.rake +61 -0
- metadata +263 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9322dedfad594507282b8a475531a48ed6c9cae16bb098e115a8a71c9c88485
|
4
|
+
data.tar.gz: b44299bb873d6a07028eca918826702c63d75460a7fa7986251792dc5fae81ac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 512419c58e7eaf81cb9e6de2496cfffd72469a1e4fa5c6b07939645f2ab3b2af5e419b155cae026f5e13910fad762cdc1cdec44b282ed93ef9ac79eb77b8475b
|
7
|
+
data.tar.gz: 9f9852d9afbba64c20a855c55e6e08f196bcc6f1a5f72f379e0839a118e638a7c96b98f7c2de227835c8b38c4cf9d4abbb19b29421cb58e85ad9637086d8db2a
|
@@ -0,0 +1,41 @@
|
|
1
|
+
name: Test
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ master ]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
build:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
strategy:
|
13
|
+
matrix:
|
14
|
+
ruby:
|
15
|
+
- '2.5.x'
|
16
|
+
- '2.6.x'
|
17
|
+
appraisal:
|
18
|
+
- 'rails-5.2'
|
19
|
+
- 'rails-6.0'
|
20
|
+
- 'semantic_logger-3.4'
|
21
|
+
- 'semantic_logger-4.6'
|
22
|
+
- 'semantic_logger-4.7.0'
|
23
|
+
- 'semantic_logger-4.7.2'
|
24
|
+
steps:
|
25
|
+
- name: Setup System
|
26
|
+
run: sudo apt-get install libsqlite3-dev
|
27
|
+
- uses: actions/checkout@v2
|
28
|
+
- uses: zhulik/redis-action@1.1.0
|
29
|
+
- name: Set up Ruby 2.6
|
30
|
+
uses: actions/setup-ruby@v1
|
31
|
+
with:
|
32
|
+
ruby-version: ${{ matrix.ruby }}
|
33
|
+
- name: Build and test with Rake
|
34
|
+
env:
|
35
|
+
APPRAISAL_CONTEXT: ${{ matrix.appraisal }}
|
36
|
+
run: |
|
37
|
+
gem install bundler
|
38
|
+
bundle install --jobs 4 --retry 3
|
39
|
+
bundle exec rubocop
|
40
|
+
bundle exec appraisal ${APPRAISAL_CONTEXT} bundle
|
41
|
+
bundle exec appraisal ${APPRAISAL_CONTEXT} rspec
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/Appraisals
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
appraise 'rails-5.2' do
|
4
|
+
gem 'rails', '5.2'
|
5
|
+
end
|
6
|
+
|
7
|
+
appraise 'rails-6.0' do
|
8
|
+
gem 'rails', '6.0'
|
9
|
+
end
|
10
|
+
|
11
|
+
appraise 'semantic_logger-3.4' do
|
12
|
+
gem 'semantic_logger', '3.4.1'
|
13
|
+
end
|
14
|
+
|
15
|
+
appraise 'semantic_logger-4.6' do
|
16
|
+
gem 'semantic_logger', '4.6.1'
|
17
|
+
end
|
18
|
+
|
19
|
+
appraise 'semantic_logger-4.7.0' do
|
20
|
+
gem 'semantic_logger', '4.7.0'
|
21
|
+
end
|
22
|
+
|
23
|
+
appraise 'semantic_logger-4.7.2' do
|
24
|
+
gem 'semantic_logger', '4.7.2'
|
25
|
+
end
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## [v0.4.0](https://github.com/keypup-io/cloudenvoy/tree/v0.4.0) (2020-10-05)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/keypup-io/cloudenvoy/compare/v0.3.1...v0.4.0)
|
6
|
+
|
7
|
+
**Bug fix:**
|
8
|
+
- Logging: fix log processing with `semantic_logger` `v4.7.2`. Accept any args on block passed to the logger.
|
9
|
+
|
10
|
+
## [v0.3.1](https://github.com/keypup-io/cloudenvoy/tree/v0.3.1) (2020-10-05)
|
11
|
+
|
12
|
+
[Full Changelog](https://github.com/keypup-io/cloudenvoy/compare/v0.3.0...v0.3.1)
|
13
|
+
|
14
|
+
**Improvements:**
|
15
|
+
- Development: auto-create topics in development mode when registering subscriptions.
|
16
|
+
|
17
|
+
## [v0.3.0](https://github.com/keypup-io/cloudenvoy/tree/v0.3.0) (2020-09-26)
|
18
|
+
|
19
|
+
[Full Changelog](https://github.com/keypup-io/cloudenvoy/compare/v0.2.0...v0.3.0)
|
20
|
+
|
21
|
+
**Bug fix:**
|
22
|
+
- Subscriptions: fix creation. `v0.2.0` introduced a bug as part of support subscription options (e.g. `retain_acked`)
|
23
|
+
|
24
|
+
## [v0.2.0](https://github.com/keypup-io/cloudenvoy/tree/v0.2.0) (2020-09-22)
|
25
|
+
|
26
|
+
[Full Changelog](https://github.com/keypup-io/cloudenvoy/compare/v0.1.0...v0.2.0)
|
27
|
+
|
28
|
+
**Improvements:**
|
29
|
+
- Subscriptions: support creation options such as `retained_acked` and `deadline`
|
30
|
+
|
31
|
+
## [v0.1.0](https://github.com/keypup-io/cloudenvoy/tree/v0.1.0) (2020-09-16)
|
32
|
+
Initial release
|
data/Gemfile.lock
CHANGED
@@ -1,19 +1,189 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
cloudenvoy (0.
|
4
|
+
cloudenvoy (0.4.0)
|
5
|
+
activesupport
|
6
|
+
google-cloud-pubsub (~> 2.0)
|
7
|
+
jwt
|
8
|
+
retriable
|
5
9
|
|
6
10
|
GEM
|
7
11
|
remote: https://rubygems.org/
|
8
12
|
specs:
|
13
|
+
actioncable (6.0.3.2)
|
14
|
+
actionpack (= 6.0.3.2)
|
15
|
+
nio4r (~> 2.0)
|
16
|
+
websocket-driver (>= 0.6.1)
|
17
|
+
actionmailbox (6.0.3.2)
|
18
|
+
actionpack (= 6.0.3.2)
|
19
|
+
activejob (= 6.0.3.2)
|
20
|
+
activerecord (= 6.0.3.2)
|
21
|
+
activestorage (= 6.0.3.2)
|
22
|
+
activesupport (= 6.0.3.2)
|
23
|
+
mail (>= 2.7.1)
|
24
|
+
actionmailer (6.0.3.2)
|
25
|
+
actionpack (= 6.0.3.2)
|
26
|
+
actionview (= 6.0.3.2)
|
27
|
+
activejob (= 6.0.3.2)
|
28
|
+
mail (~> 2.5, >= 2.5.4)
|
29
|
+
rails-dom-testing (~> 2.0)
|
30
|
+
actionpack (6.0.3.2)
|
31
|
+
actionview (= 6.0.3.2)
|
32
|
+
activesupport (= 6.0.3.2)
|
33
|
+
rack (~> 2.0, >= 2.0.8)
|
34
|
+
rack-test (>= 0.6.3)
|
35
|
+
rails-dom-testing (~> 2.0)
|
36
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
37
|
+
actiontext (6.0.3.2)
|
38
|
+
actionpack (= 6.0.3.2)
|
39
|
+
activerecord (= 6.0.3.2)
|
40
|
+
activestorage (= 6.0.3.2)
|
41
|
+
activesupport (= 6.0.3.2)
|
42
|
+
nokogiri (>= 1.8.5)
|
43
|
+
actionview (6.0.3.2)
|
44
|
+
activesupport (= 6.0.3.2)
|
45
|
+
builder (~> 3.1)
|
46
|
+
erubi (~> 1.4)
|
47
|
+
rails-dom-testing (~> 2.0)
|
48
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
49
|
+
activejob (6.0.3.2)
|
50
|
+
activesupport (= 6.0.3.2)
|
51
|
+
globalid (>= 0.3.6)
|
52
|
+
activemodel (6.0.3.2)
|
53
|
+
activesupport (= 6.0.3.2)
|
54
|
+
activerecord (6.0.3.2)
|
55
|
+
activemodel (= 6.0.3.2)
|
56
|
+
activesupport (= 6.0.3.2)
|
57
|
+
activestorage (6.0.3.2)
|
58
|
+
actionpack (= 6.0.3.2)
|
59
|
+
activejob (= 6.0.3.2)
|
60
|
+
activerecord (= 6.0.3.2)
|
61
|
+
marcel (~> 0.3.1)
|
62
|
+
activesupport (6.0.3.2)
|
63
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
64
|
+
i18n (>= 0.7, < 2)
|
65
|
+
minitest (~> 5.1)
|
66
|
+
tzinfo (~> 1.1)
|
67
|
+
zeitwerk (~> 2.2, >= 2.2.2)
|
68
|
+
addressable (2.7.0)
|
69
|
+
public_suffix (>= 2.0.2, < 5.0)
|
70
|
+
appraisal (2.3.0)
|
71
|
+
bundler
|
72
|
+
rake
|
73
|
+
thor (>= 0.14.0)
|
9
74
|
ast (2.4.1)
|
75
|
+
builder (3.2.4)
|
76
|
+
concurrent-ruby (1.1.6)
|
77
|
+
crack (0.4.3)
|
78
|
+
safe_yaml (~> 1.0.0)
|
79
|
+
crass (1.0.6)
|
10
80
|
diff-lcs (1.4.2)
|
81
|
+
erubi (1.9.0)
|
82
|
+
faraday (1.0.1)
|
83
|
+
multipart-post (>= 1.2, < 3)
|
84
|
+
gapic-common (0.3.4)
|
85
|
+
google-protobuf (~> 3.12, >= 3.12.2)
|
86
|
+
googleapis-common-protos (>= 1.3.9, < 2.0)
|
87
|
+
googleapis-common-protos-types (>= 1.0.4, < 2.0)
|
88
|
+
googleauth (~> 0.9)
|
89
|
+
grpc (~> 1.25)
|
90
|
+
globalid (0.4.2)
|
91
|
+
activesupport (>= 4.2.0)
|
92
|
+
google-cloud-core (1.5.0)
|
93
|
+
google-cloud-env (~> 1.0)
|
94
|
+
google-cloud-errors (~> 1.0)
|
95
|
+
google-cloud-env (1.3.3)
|
96
|
+
faraday (>= 0.17.3, < 2.0)
|
97
|
+
google-cloud-errors (1.0.1)
|
98
|
+
google-cloud-pubsub (2.0.0)
|
99
|
+
concurrent-ruby (~> 1.1)
|
100
|
+
google-cloud-core (~> 1.5)
|
101
|
+
google-cloud-pubsub-v1 (~> 0.0)
|
102
|
+
google-cloud-pubsub-v1 (0.1.2)
|
103
|
+
gapic-common (~> 0.3)
|
104
|
+
google-cloud-errors (~> 1.0)
|
105
|
+
grpc-google-iam-v1 (>= 0.6.10, < 2.0)
|
106
|
+
google-protobuf (3.13.0-universal-darwin)
|
107
|
+
googleapis-common-protos (1.3.10)
|
108
|
+
google-protobuf (~> 3.11)
|
109
|
+
googleapis-common-protos-types (>= 1.0.5, < 2.0)
|
110
|
+
grpc (~> 1.27)
|
111
|
+
googleapis-common-protos-types (1.0.5)
|
112
|
+
google-protobuf (~> 3.11)
|
113
|
+
googleauth (0.13.1)
|
114
|
+
faraday (>= 0.17.3, < 2.0)
|
115
|
+
jwt (>= 1.4, < 3.0)
|
116
|
+
memoist (~> 0.16)
|
117
|
+
multi_json (~> 1.11)
|
118
|
+
os (>= 0.9, < 2.0)
|
119
|
+
signet (~> 0.14)
|
120
|
+
grpc (1.32.0-universal-darwin)
|
121
|
+
google-protobuf (~> 3.13)
|
122
|
+
googleapis-common-protos-types (~> 1.0)
|
123
|
+
grpc-google-iam-v1 (0.6.10)
|
124
|
+
google-protobuf (~> 3.11)
|
125
|
+
googleapis-common-protos (>= 1.3.10, < 2.0)
|
126
|
+
grpc (~> 1.27)
|
127
|
+
hashdiff (1.0.1)
|
128
|
+
i18n (1.8.5)
|
129
|
+
concurrent-ruby (~> 1.0)
|
11
130
|
jaro_winkler (1.5.4)
|
131
|
+
jwt (2.2.2)
|
132
|
+
loofah (2.6.0)
|
133
|
+
crass (~> 1.0.2)
|
134
|
+
nokogiri (>= 1.5.9)
|
135
|
+
mail (2.7.1)
|
136
|
+
mini_mime (>= 0.1.1)
|
137
|
+
marcel (0.3.3)
|
138
|
+
mimemagic (~> 0.3.2)
|
139
|
+
memoist (0.16.2)
|
140
|
+
method_source (1.0.0)
|
141
|
+
mimemagic (0.3.5)
|
142
|
+
mini_mime (1.0.2)
|
143
|
+
mini_portile2 (2.4.0)
|
144
|
+
minitest (5.14.1)
|
145
|
+
multi_json (1.15.0)
|
146
|
+
multipart-post (2.1.1)
|
147
|
+
nio4r (2.5.2)
|
148
|
+
nokogiri (1.10.10)
|
149
|
+
mini_portile2 (~> 2.4.0)
|
150
|
+
os (1.1.1)
|
12
151
|
parallel (1.19.2)
|
13
152
|
parser (2.7.1.4)
|
14
153
|
ast (~> 2.4.1)
|
154
|
+
public_suffix (4.0.5)
|
155
|
+
rack (2.2.3)
|
156
|
+
rack-test (1.1.0)
|
157
|
+
rack (>= 1.0, < 3)
|
158
|
+
rails (6.0.3.2)
|
159
|
+
actioncable (= 6.0.3.2)
|
160
|
+
actionmailbox (= 6.0.3.2)
|
161
|
+
actionmailer (= 6.0.3.2)
|
162
|
+
actionpack (= 6.0.3.2)
|
163
|
+
actiontext (= 6.0.3.2)
|
164
|
+
actionview (= 6.0.3.2)
|
165
|
+
activejob (= 6.0.3.2)
|
166
|
+
activemodel (= 6.0.3.2)
|
167
|
+
activerecord (= 6.0.3.2)
|
168
|
+
activestorage (= 6.0.3.2)
|
169
|
+
activesupport (= 6.0.3.2)
|
170
|
+
bundler (>= 1.3.0)
|
171
|
+
railties (= 6.0.3.2)
|
172
|
+
sprockets-rails (>= 2.0.0)
|
173
|
+
rails-dom-testing (2.0.3)
|
174
|
+
activesupport (>= 4.2.0)
|
175
|
+
nokogiri (>= 1.6)
|
176
|
+
rails-html-sanitizer (1.3.0)
|
177
|
+
loofah (~> 2.3)
|
178
|
+
railties (6.0.3.2)
|
179
|
+
actionpack (= 6.0.3.2)
|
180
|
+
activesupport (= 6.0.3.2)
|
181
|
+
method_source
|
182
|
+
rake (>= 0.8.7)
|
183
|
+
thor (>= 0.20.3, < 2.0)
|
15
184
|
rainbow (3.0.0)
|
16
185
|
rake (13.0.1)
|
186
|
+
retriable (3.1.2)
|
17
187
|
rspec (3.9.0)
|
18
188
|
rspec-core (~> 3.9.0)
|
19
189
|
rspec-expectations (~> 3.9.0)
|
@@ -26,6 +196,14 @@ GEM
|
|
26
196
|
rspec-mocks (3.9.1)
|
27
197
|
diff-lcs (>= 1.2.0, < 2.0)
|
28
198
|
rspec-support (~> 3.9.0)
|
199
|
+
rspec-rails (4.0.1)
|
200
|
+
actionpack (>= 4.2)
|
201
|
+
activesupport (>= 4.2)
|
202
|
+
railties (>= 4.2)
|
203
|
+
rspec-core (~> 3.9)
|
204
|
+
rspec-expectations (~> 3.9)
|
205
|
+
rspec-mocks (~> 3.9)
|
206
|
+
rspec-support (~> 3.9)
|
29
207
|
rspec-support (3.9.3)
|
30
208
|
rubocop (0.76.0)
|
31
209
|
jaro_winkler (~> 1.5.1)
|
@@ -37,17 +215,53 @@ GEM
|
|
37
215
|
rubocop-rspec (1.37.0)
|
38
216
|
rubocop (>= 0.68.1)
|
39
217
|
ruby-progressbar (1.10.1)
|
218
|
+
safe_yaml (1.0.5)
|
219
|
+
semantic_logger (4.7.2)
|
220
|
+
concurrent-ruby (~> 1.0)
|
221
|
+
signet (0.14.0)
|
222
|
+
addressable (~> 2.3)
|
223
|
+
faraday (>= 0.17.3, < 2.0)
|
224
|
+
jwt (>= 1.5, < 3.0)
|
225
|
+
multi_json (~> 1.10)
|
226
|
+
sprockets (4.0.2)
|
227
|
+
concurrent-ruby (~> 1.0)
|
228
|
+
rack (> 1, < 3)
|
229
|
+
sprockets-rails (3.2.1)
|
230
|
+
actionpack (>= 4.0)
|
231
|
+
activesupport (>= 4.0)
|
232
|
+
sprockets (>= 3.0.0)
|
233
|
+
sqlite3 (1.4.2)
|
234
|
+
thor (1.0.1)
|
235
|
+
thread_safe (0.3.6)
|
236
|
+
timecop (0.9.1)
|
237
|
+
tzinfo (1.2.7)
|
238
|
+
thread_safe (~> 0.1)
|
40
239
|
unicode-display_width (1.6.1)
|
240
|
+
webmock (3.8.3)
|
241
|
+
addressable (>= 2.3.6)
|
242
|
+
crack (>= 0.3.2)
|
243
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
244
|
+
websocket-driver (0.7.3)
|
245
|
+
websocket-extensions (>= 0.1.0)
|
246
|
+
websocket-extensions (0.1.5)
|
247
|
+
zeitwerk (2.4.0)
|
41
248
|
|
42
249
|
PLATFORMS
|
43
250
|
ruby
|
44
251
|
|
45
252
|
DEPENDENCIES
|
253
|
+
appraisal
|
46
254
|
cloudenvoy!
|
255
|
+
rails
|
47
256
|
rake (>= 12.3.3)
|
48
257
|
rspec (~> 3.0)
|
258
|
+
rspec-rails
|
49
259
|
rubocop (= 0.76.0)
|
50
260
|
rubocop-rspec (= 1.37.0)
|
261
|
+
semantic_logger
|
262
|
+
sqlite3
|
263
|
+
timecop
|
264
|
+
webmock
|
51
265
|
|
52
266
|
BUNDLED WITH
|
53
267
|
2.1.4
|
data/README.md
CHANGED
@@ -1,8 +1,33 @@
|
|
1
|
+
![Build Status](https://github.com/keypup-io/cloudenvoy/workflows/Test/badge.svg) [![Gem Version](https://badge.fury.io/rb/cloudenvoy.svg)](https://badge.fury.io/rb/cloudenvoy)
|
2
|
+
|
3
|
+
**Note**: this gem is currently in alpha stage and has not been tested in production yet.
|
4
|
+
|
1
5
|
# Cloudenvoy
|
2
6
|
|
3
|
-
|
7
|
+
Cross-application messaging framework for GCP Pub/Sub.
|
8
|
+
|
9
|
+
Cloudenvoy provides an easy to use interface to GCP Pub/Sub. Using Cloudenvoy you can simplify cross-application event messaging by using a publish/subscribe approach. Pub/Sub is particularly suited for micro-service architectures where a great number of components need to be aware of other components' activities. In these architectures using point to point communication via API can quickly become messy and hard to maintain due to the number of interconnections to maintain.
|
10
|
+
|
11
|
+
Pub/Sub solves that event distribution problem by allowing developers to define topics, publishers and subscribers to distribute and process event messages. Cloudenvoy furthers simplifies the process of setting up Pub/Sub by giving developers an object-oriented way of managing publishers and subscribers.
|
12
|
+
|
13
|
+
Cloudenvoy works with the local pub/sub emulator as well, meaning that you can work offline without access to GCP.
|
14
|
+
|
15
|
+
## Summary
|
4
16
|
|
5
|
-
|
17
|
+
1. [Installation](#installation)
|
18
|
+
2. [Get started with Rails](#get-started-with-rails)
|
19
|
+
3. [Configuring Cloudenvoy](#configuring-cloudenvoy)
|
20
|
+
1. [Pub/Sub authentication & permissions](#pubsub-authentication--permissions)
|
21
|
+
2. [Cloudenvoy initializer](#cloudenvoy-initializer)
|
22
|
+
4. [Creating topics and subscriptions](#creating-topics-and-subscriptions)
|
23
|
+
1. [Sending messages](#sending-messages)
|
24
|
+
2. [Publisher implementation layout](#publisher-implementation-layout)
|
25
|
+
5. [Receiving messages](#receiving-messages)
|
26
|
+
6. [Error Handling](#error-handling)
|
27
|
+
7. [Testing](#testing)
|
28
|
+
1. [Test helper setup](#test-helper-setup)
|
29
|
+
2. [In-memory queues](#in-memory-queues)
|
30
|
+
3. [Unit tests](#unit-tests)
|
6
31
|
|
7
32
|
## Installation
|
8
33
|
|
@@ -20,9 +45,559 @@ Or install it yourself as:
|
|
20
45
|
|
21
46
|
$ gem install cloudenvoy
|
22
47
|
|
23
|
-
##
|
48
|
+
## Get started with Rails
|
49
|
+
|
50
|
+
Cloudenvoy is pre-integrated with Rails. Follow the steps below to get started.
|
51
|
+
|
52
|
+
Install the pub/sub local emulator
|
53
|
+
```bash
|
54
|
+
gcloud components install pubsub-emulator
|
55
|
+
gcloud components update
|
56
|
+
```
|
57
|
+
|
58
|
+
Add the following initializer
|
59
|
+
```ruby
|
60
|
+
# config/initializers/cloudenvoy.rb
|
61
|
+
|
62
|
+
Cloudenvoy.configure do |config|
|
63
|
+
#
|
64
|
+
# GCP Configuration
|
65
|
+
#
|
66
|
+
config.gcp_project_id = 'some-project'
|
67
|
+
config.gcp_sub_prefix = 'my-app'
|
68
|
+
|
69
|
+
#
|
70
|
+
# Adapt the server port to be the one used by your Rails web process
|
71
|
+
#
|
72
|
+
config.processor_host = 'http://localhost:3000'
|
73
|
+
|
74
|
+
#
|
75
|
+
# If you do not have any Rails secret_key_base defined, uncomment the following
|
76
|
+
# This secret is used to authenticate messages sent to the processing endpoint
|
77
|
+
# of your application.
|
78
|
+
#
|
79
|
+
# config.secret = 'some-long-token'
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
Define a publisher:
|
84
|
+
```ruby
|
85
|
+
# app/publishers/dummy_publisher.rb
|
86
|
+
|
87
|
+
class DummyPublisher
|
88
|
+
include Cloudenvoy::Publisher
|
89
|
+
|
90
|
+
cloudenvoy_options topic: 'test-msgs'
|
91
|
+
|
92
|
+
# Format the message payload. The payload can be a hash
|
93
|
+
# or a string.
|
94
|
+
def payload(msg)
|
95
|
+
{
|
96
|
+
type: 'message',
|
97
|
+
content: msg
|
98
|
+
}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
```
|
102
|
+
|
103
|
+
Define a subscriber:
|
104
|
+
```ruby
|
105
|
+
# app/subscribers/dummy_subscriber.rb
|
106
|
+
|
107
|
+
class HelloSubscriber
|
108
|
+
include Cloudenvoy::Subscriber
|
109
|
+
|
110
|
+
cloudenvoy_options topics: ['test-msgs']
|
111
|
+
|
112
|
+
# Do something with the message
|
113
|
+
def process(message)
|
114
|
+
logger.info("Received message #{message.payload.dig('content')}")
|
115
|
+
end
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
119
|
+
Launch the pub/sub emulator:
|
120
|
+
```bash
|
121
|
+
gcloud beta emulators pubsub start
|
122
|
+
```
|
123
|
+
|
124
|
+
Use cloudenvoy to setup your topic and subscription
|
125
|
+
```bash
|
126
|
+
bundle exec rake cloudenvoy:setup
|
127
|
+
```
|
128
|
+
|
129
|
+
Launch Rails
|
130
|
+
```bash
|
131
|
+
rails s -p 3000
|
132
|
+
```
|
133
|
+
|
134
|
+
Open a Rails console and send a message
|
135
|
+
```ruby
|
136
|
+
DummyPublisher.publish('Hello pub/sub')
|
137
|
+
```
|
138
|
+
|
139
|
+
Your Rails logs should display the following:
|
140
|
+
```log
|
141
|
+
Started POST "/cloudenvoy/receive?token=1234" for 66.102.6.140 at 2020-09-16 11:12:47 +0200
|
142
|
+
Processing by Cloudenvoy::SubscriberController#receive as JSON
|
143
|
+
Parameters: {"message"=>{"attributes"=>{"kind"=>"hello"}, "data"=>"eyJ0eXBlIjoibWVzc2FnZSIsImNvbnRlbnQiOiJIZWxsbyBmcmllbmQifQ==", "messageId"=>"1501653492745522", "message_id"=>"1501653492745522", "publishTime"=>"2020-09-16T09:12:45.214Z", "publish_time"=>"2020-09-16T09:12:45.214Z"}, "subscription"=>"projects/keypup-dev/subscriptions/my-app.hello_subscriber.test-msgs", "token"=>"eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MDAyNDc0OTh9.5SbVsDCZLcyoFXseCpPuvE7KY7WXqIQtO6ceoFXcrdw", "subscriber"=>{"message"=>{"attributes"=>{"kind"=>"hello"}, "data"=>"eyJ0eXBlIjoibWVzc2FnZSIsImNvbnRlbnQiOiJIZWxsbyBmcmllbmQifQ==", "messageId"=>"1501653492745522", "message_id"=>"1501653492745522", "publishTime"=>"2020-09-16T09:12:45.214Z", "publish_time"=>"2020-09-16T09:12:45.214Z"}, "subscription"=>"projects/keypup-dev/subscriptions/my-app.hello_subscriber.test-msgs"}}
|
144
|
+
[Cloudenvoy][HelloSubscriber][1501653492745522] Processing message... -- {:id=>"1501653492745522", :metadata=>{}, :topic=>"test-msgs"}
|
145
|
+
[Cloudenvoy][HelloSubscriber][1501653492745522] Received message Hello pub/sub -- {:id=>"1501653492745522", :metadata=>{}, :topic=>"test-msgs"}
|
146
|
+
[Cloudenvoy][HelloSubscriber][1501653492745522] Processing done after 0.001s -- {:id=>"1501653492745522", :metadata=>{}, :topic=>"test-msgs", :duration=>0.001}
|
147
|
+
Completed 204 No Content in 1ms (ActiveRecord: 0.0ms | Allocations: 500)
|
148
|
+
```
|
149
|
+
|
150
|
+
Hurray! Your published message was immediately processed by the subscriber.
|
151
|
+
|
152
|
+
## Configuring Cloudenvoy
|
153
|
+
|
154
|
+
### Pub/Sub authentication & permissions
|
155
|
+
|
156
|
+
The Google Cloud library authenticates via the Google Cloud SDK by default. If you do not have it setup then we recommend you [install it](https://cloud.google.com/sdk/docs/quickstarts).
|
157
|
+
|
158
|
+
Other options are available such as using a service account. You can see all authentication options in the [Google Cloud Authentication guide](https://github.com/googleapis/google-cloud-ruby/blob/master/google-cloud-bigquery/AUTHENTICATION.md).
|
159
|
+
|
160
|
+
In order to function properly Cloudenvoy requires the authenticated account to have the following IAM permissions:
|
161
|
+
- `pubsub.subscriptions.create`
|
162
|
+
- `pubsub.subscriptions.get`
|
163
|
+
- `pubsub.topics.create`
|
164
|
+
- `pubsub.topics.get`
|
165
|
+
- `pubsub.topics.publish`
|
166
|
+
|
167
|
+
To get started quickly you can add the `roles/pubsub.admin` role to your account via the [IAM Console](https://console.cloud.google.com/iam-admin/iam). This is not required if your account is a project admin account.
|
168
|
+
|
169
|
+
### Cloudenvoy initializer
|
170
|
+
|
171
|
+
The gem can be configured through an initializer. See below all the available configuration options.
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
# config/initializers/cloudenvoy.rb
|
175
|
+
|
176
|
+
Cloudenvoy.configure do |config|
|
177
|
+
#
|
178
|
+
# If you do not have any Rails secret_key_base defined, uncomment the following.
|
179
|
+
# This secret is used to authenticate messages sent to the processing endpoint
|
180
|
+
# of your application.
|
181
|
+
#
|
182
|
+
# Default with Rails: Rails.application.credentials.secret_key_base
|
183
|
+
#
|
184
|
+
# config.secret = 'some-long-token'
|
185
|
+
|
186
|
+
#
|
187
|
+
# GCP Configuration
|
188
|
+
#
|
189
|
+
config.gcp_project_id = 'some-project'
|
190
|
+
|
191
|
+
#
|
192
|
+
# Specify the namespace for your subscriptions
|
193
|
+
#
|
194
|
+
# The gem attempts to keep GCP subscriptions organized by
|
195
|
+
# properly namespacing them. Each subscription has the following
|
196
|
+
# format:
|
197
|
+
# > projects/<gcp_project_id>/subscriptions/<gcp_sub_prefix>.<subscriber_class>.<topic>
|
198
|
+
#
|
199
|
+
config.gcp_sub_prefix = 'my-app'
|
200
|
+
|
201
|
+
#
|
202
|
+
# Specify the publicly accessible host for your application
|
203
|
+
#
|
204
|
+
# > E.g. in development, using the pub/sub local emulator
|
205
|
+
# config.processor_host = 'http://localhost:3000'
|
206
|
+
#
|
207
|
+
# > E.g. in development, using `config.mode = :production` and ngrok
|
208
|
+
# config.processor_host = 'https://111111.ngrok.io'
|
209
|
+
#
|
210
|
+
config.processor_host = 'https://app.mydomain.com'
|
211
|
+
|
212
|
+
#
|
213
|
+
# Specify the mode of operation:
|
214
|
+
# - :development => messages will be pushed to the local pub/sub emulator
|
215
|
+
# - :production => messages will be pushed to Google Cloud Pub/Sub. Requires a publicly accessible domain.
|
216
|
+
#
|
217
|
+
# Defaults to :development unless CLOUDENVOY_ENV or RAILS_ENV or RACK_ENV is set to something else.
|
218
|
+
#
|
219
|
+
# config.mode = Rails.env.production? || Rails.env.my_other_env? ? :production : :development
|
220
|
+
|
221
|
+
#
|
222
|
+
# Specify the logger to use
|
223
|
+
#
|
224
|
+
# Default with Rails: Rails.logger
|
225
|
+
# Default without Rails: Logger.new(STDOUT)
|
226
|
+
#
|
227
|
+
# config.logger = MyLogger.new(STDOUT)
|
228
|
+
end
|
229
|
+
```
|
230
|
+
|
231
|
+
### Creating topics and subscriptions
|
232
|
+
|
233
|
+
Topics and subscriptions can be created using the provided Rake tasks:
|
234
|
+
```bash
|
235
|
+
# Setup publishers (topics) and subscribers (subscriptions) in one go
|
236
|
+
bundle exec rake cloudenvoy:setup
|
237
|
+
|
238
|
+
# Or set them up individually
|
239
|
+
bundle exec rake cloudenvoy:setup_publishers
|
240
|
+
bundle exec rake cloudenvoy:setup_subscribers
|
241
|
+
```
|
242
|
+
|
243
|
+
For non-rails applications you can run the following in a console to setup your publishers and subscribers:
|
244
|
+
```ruby
|
245
|
+
DummyPublisher.setup
|
246
|
+
DummySubscriber.setup
|
247
|
+
```
|
248
|
+
|
249
|
+
## Publishing messages
|
250
|
+
|
251
|
+
### Sending messages
|
252
|
+
|
253
|
+
Cloudenvoy provides a helper method to publish arbitrary messages to any topic.
|
254
|
+
```ruby
|
255
|
+
Cloudenvoy.publish('my-topic', { 'some' => 'payload' }, { 'optional' => 'message attribute' })
|
256
|
+
```
|
257
|
+
|
258
|
+
This helper is useful for sending basic messages however it is not the preferred way of sending messages as you will quickly clutter your application with message formatting logic over time.
|
259
|
+
|
260
|
+
Cloudenvoy provides an object-oriented way of sending messages allowing developers to separate their core business logic from any kind of message formatting logic. These are called `Publishers`.
|
261
|
+
|
262
|
+
The example below shows you how to publish new users to a topic using Cloudenvoy publishers:
|
263
|
+
```ruby
|
264
|
+
# app/publishers/user_publisher.rb
|
265
|
+
|
266
|
+
# The publisher is responsible for configuring and formatting
|
267
|
+
# the pub/sub message.
|
268
|
+
class UserPublisher
|
269
|
+
include Cloudenvoy::Publisher
|
270
|
+
|
271
|
+
cloudenvoy_options topic: 'system-users'
|
272
|
+
|
273
|
+
# Publishers must at least implement the `payload` method,
|
274
|
+
# which specifies how the message should be formatted.
|
275
|
+
def payload(user)
|
276
|
+
{
|
277
|
+
id: user.id,
|
278
|
+
name: user.name,
|
279
|
+
email: user.email
|
280
|
+
}
|
281
|
+
end
|
282
|
+
end
|
283
|
+
```
|
284
|
+
|
285
|
+
Then in your user model you can do the following:
|
286
|
+
```ruby
|
287
|
+
# app/users/user_publisher.rb
|
288
|
+
|
289
|
+
class User < ApplicationRecord
|
290
|
+
after_create :publish_user
|
291
|
+
|
292
|
+
private
|
293
|
+
|
294
|
+
# Publish users after they have been created
|
295
|
+
def publish_user
|
296
|
+
UserPublisher.publish(self)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
```
|
300
|
+
|
301
|
+
### Publisher implementation layout
|
302
|
+
|
303
|
+
A full publisher implementation looks like this:
|
304
|
+
```ruby
|
305
|
+
class MyPublisher
|
306
|
+
include Cloudenvoy::Publisher
|
307
|
+
|
308
|
+
# The topic option defines the default topic messages will be
|
309
|
+
# sent to. The publishing topic can be overriden on a per message
|
310
|
+
# basis. See the #topic method below.
|
311
|
+
cloudenvoy_options topic: 'my-topic'
|
312
|
+
|
313
|
+
# Evaluate the topic at runtime based on publishing arguments.
|
314
|
+
# Returning `nil` makes the publisher use the default topic
|
315
|
+
# defined via cloudenvoy_options.
|
316
|
+
#
|
317
|
+
# Note: runtime topics do not get created by the rake tasks. You
|
318
|
+
# must create them manually at this stage.
|
319
|
+
def topic(arg1, arg2)
|
320
|
+
arg1 == 'other' ? 'some-other-topic' : nil
|
321
|
+
end
|
322
|
+
|
323
|
+
# Attach pub/sub attributes to the message. Pub/sub attributes
|
324
|
+
# can be used for message filtering.
|
325
|
+
def metadata(arg1, arg2)
|
326
|
+
{ reference: "#{arg1}_#{arg2}" }
|
327
|
+
end
|
328
|
+
|
329
|
+
# Publishers must at least implement the `payload` method,
|
330
|
+
# which specifies how arguments should be transformed into
|
331
|
+
# a message payload (Hash or String).
|
332
|
+
def payload(arg1, arg2)
|
333
|
+
{
|
334
|
+
foo: arg1,
|
335
|
+
bar: arg2
|
336
|
+
}
|
337
|
+
end
|
338
|
+
|
339
|
+
# This hook is invoked when the message fails to be formatted and published.
|
340
|
+
# If something wrong happens in the methods above, this hook will be triggered.
|
341
|
+
def on_error(error)
|
342
|
+
logger.error("Oops! Something wrong happened!")
|
343
|
+
end
|
344
|
+
end
|
345
|
+
```
|
346
|
+
|
347
|
+
## Receiving messages
|
348
|
+
|
349
|
+
After you have subscribed to a topic, Pub/Sub sends messages to your application via webhook on the `/cloudenvoy/receive` endpoint. Cloudenvoy then automatically dispatches the message to the right subscriber for processing.
|
350
|
+
|
351
|
+
Following up on the previous user publishing example, you might define the following subscriber in another Rails application:
|
352
|
+
|
353
|
+
```ruby
|
354
|
+
# app/subscribers/user_subscriber.rb
|
355
|
+
|
356
|
+
class UserSubscriber
|
357
|
+
include Cloudenvoy::Subscriber
|
358
|
+
|
359
|
+
# Subscribers can subscribe to multiple topics
|
360
|
+
#
|
361
|
+
# You can subscribe to multiple topics:
|
362
|
+
# > cloudenvoy_options topics: ['system-users', 'events']
|
363
|
+
#
|
364
|
+
# You can specify subscription options for each topic
|
365
|
+
# by passing a hash (target: v0.2.0)
|
366
|
+
#
|
367
|
+
# > cloudenvoy_options topics: ['system-users', { name: 'events', retain_acked: true }]
|
368
|
+
#
|
369
|
+
# See the Pub/Sub documentation of the list of available subscription options:
|
370
|
+
# https://googleapis.dev/ruby/google-cloud-pubsub/latest/Google/Cloud/PubSub/Topic.html#subscribe-instance_method
|
371
|
+
#
|
372
|
+
cloudenvoy_options topic: 'system-users'
|
373
|
+
|
374
|
+
# Create the user locally if it does not exist already
|
375
|
+
#
|
376
|
+
# A message has the following attributes:
|
377
|
+
# id: the pub/sub message id
|
378
|
+
# payload: the content of the message (String or Hash)
|
379
|
+
# metadata: the pub/sub message attributes
|
380
|
+
# sub_uri: the pub/sub subscription URI
|
381
|
+
# topic: the topic the message comes from
|
382
|
+
#
|
383
|
+
def process(message)
|
384
|
+
payload = message.payload
|
385
|
+
|
386
|
+
User.create_or_find_by(system_id: payload['id']) do |u|
|
387
|
+
u.first_name = payload['name']
|
388
|
+
u.email = payload['email']
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
# This hook will be invoked if the message processing fails
|
393
|
+
def on_error(error)
|
394
|
+
logger.error("The following error happened: #{error}")
|
395
|
+
end
|
396
|
+
end
|
397
|
+
```
|
398
|
+
|
399
|
+
## Logging
|
400
|
+
There are several options available to configure logging and logging context.
|
401
|
+
|
402
|
+
### Configuring a logger
|
403
|
+
Cloudenvoy uses `Rails.logger` if Rails is available and falls back on a plain ruby logger `Logger.new(STDOUT)` if not.
|
404
|
+
|
405
|
+
It is also possible to configure your own logger. For example you can setup Cloudenvoy with [semantic_logger](http://rocketjob.github.io/semantic_logger) by doing the following in your initializer:
|
406
|
+
```ruby
|
407
|
+
# config/initializers/cloudenvoy.rb
|
408
|
+
|
409
|
+
Cloudenvoy.configure do |config|
|
410
|
+
config.logger = SemanticLogger[Cloudenvoy]
|
411
|
+
end
|
412
|
+
```
|
413
|
+
|
414
|
+
### Logging context
|
415
|
+
Cloudenvoy provides publisher/subscriber contextual information to the `logger` methods.
|
416
|
+
|
417
|
+
For example:
|
418
|
+
```ruby
|
419
|
+
# app/subscribers/dummy_subscriber.rb
|
420
|
+
|
421
|
+
class DummySubscriber
|
422
|
+
include Cloudenvoy::Subscriber
|
423
|
+
|
424
|
+
cloudenvoy_options topics: ['my-topic']
|
425
|
+
|
426
|
+
def process(message)
|
427
|
+
logger.info("Subscriber processed with #{message.inspect}. This is working!")
|
428
|
+
end
|
429
|
+
end
|
430
|
+
```
|
431
|
+
|
432
|
+
Will generate the following log with context `{:id=>..., :metadata=>..., :topic=>...}`
|
433
|
+
```log
|
434
|
+
[Cloudenvoy][DummySubscriber][1501678353930997] Subscriber processed with ###. This is working! -- {:id=>"1501678353930997", :metadata=>{"some"=>"meta"}, :topic=>"my-topic"}
|
435
|
+
```
|
436
|
+
|
437
|
+
The way contextual information is displayed depends on the logger itself. For example with [semantic_logger](http://rocketjob.github.io/semantic_logger) contextual information might not appear in the log message but show up as payload data on the log entry itself (e.g. using the fluentd adapter).
|
438
|
+
|
439
|
+
Contextual information can be customised globally and locally using a log context_processor. By default the loggers are configured this way:
|
440
|
+
```ruby
|
441
|
+
# Publishers
|
442
|
+
Cloudenvoy::PublisherLogger.log_context_processor = ->(publisher) { publisher.message&.to_h&.slice(:id, :metadata, :topic) || {} }
|
443
|
+
|
444
|
+
# Subscribers
|
445
|
+
Cloudenvoy::SubscriberLogger.log_context_processor = ->(subscriber) { subscriber.message.to_h.slice(:id, :metadata, :topic) }
|
446
|
+
```
|
447
|
+
|
448
|
+
You can decide to add a global identifier for your publisher logs using the following:
|
449
|
+
```ruby
|
450
|
+
# config/initializers/cloudenvoy.rb
|
451
|
+
|
452
|
+
Cloudenvoy::PublisherLogger.log_context_processor = lambda { |publisher|
|
453
|
+
publisher.message.to_h.slice(:id, :metadata, :topic).merge(app: 'my-app')
|
454
|
+
}
|
455
|
+
```
|
456
|
+
|
457
|
+
You could also decide to log all available context - including the message payload - for specific subscribers only:
|
458
|
+
```ruby
|
459
|
+
# app/subscribers/full_context_subscriber.rb
|
460
|
+
|
461
|
+
class FullContextSubscriber
|
462
|
+
include Cloudenvoy::Subscriber
|
463
|
+
|
464
|
+
cloudenvoy_options topics: ['my-topic'], log_context_processor: ->(s) { s.message.to_h }
|
465
|
+
|
466
|
+
def process(message)
|
467
|
+
logger.info("This log entry will have full context!")
|
468
|
+
end
|
469
|
+
end
|
470
|
+
```
|
471
|
+
|
472
|
+
See the [Cloudenvoy::Publisher](lib/cloudenvoy/publisher.rb), [Cloudenvoy::Subscriber](lib/cloudenvoy/subscriber.rb) and [Cloudenvoy::Message](lib/cloudenvoy/message.rb) for more information on attributes available to be logged in your `log_context_processor` proc.
|
473
|
+
|
474
|
+
## Error Handling
|
475
|
+
|
476
|
+
Message failures will return an HTTP error to Pub/Sub and trigger a retry at a later time. By default Pub/Sub will retry sending the message until the acknowledgment deadline expires. A number of retries can be explicitly configured by setting up a dead-letter queue.
|
477
|
+
|
478
|
+
### HTTP Error codes
|
479
|
+
|
480
|
+
When Cloudenvoy fails to process a message it returns the following HTTP error code to Pub/Sub, based on the actual reason:
|
24
481
|
|
25
|
-
|
482
|
+
| Code | Description |
|
483
|
+
|------|-------------|
|
484
|
+
| 204 | The message was processed successfully |
|
485
|
+
| 404 | The message subscriber does not exist. |
|
486
|
+
| 422 | An error occured during the processing of the message (`process` method) |
|
487
|
+
|
488
|
+
### Error callbacks
|
489
|
+
|
490
|
+
Publishers and subscribers can implement the `on_error(error)` callback to do things when a message fails to be published or received:
|
491
|
+
|
492
|
+
E.g.
|
493
|
+
```ruby
|
494
|
+
# app/publisher/handle_error_publisher.rb
|
495
|
+
|
496
|
+
class HandleErrorPublisher
|
497
|
+
include Cloudenvoy::Publisher
|
498
|
+
|
499
|
+
cloudenvoy_options topic: 'my-topic'
|
500
|
+
|
501
|
+
def payload(arg)
|
502
|
+
raise(ArgumentError)
|
503
|
+
end
|
504
|
+
|
505
|
+
# The runtime error is passed as an argument.
|
506
|
+
def on_error(error)
|
507
|
+
logger.error("The following error occured: #{error}")
|
508
|
+
end
|
509
|
+
end
|
510
|
+
```
|
511
|
+
|
512
|
+
## Testing
|
513
|
+
Cloudenvoy provides several options to test your publishers and subscribers.
|
514
|
+
|
515
|
+
### Test helper setup
|
516
|
+
Require `cloudenvoy/testing` in your `rails_helper.rb` (Rspec Rails) or `spec_helper.rb` (Rspec) or test unit helper file then enable one of the two modes:
|
517
|
+
|
518
|
+
```ruby
|
519
|
+
require 'cloudenvoy/testing'
|
520
|
+
|
521
|
+
# Mode 1 (default): Push messages to GCP Pub/Sub (env != development)
|
522
|
+
Cloudenvoy::Testing.enable!
|
523
|
+
|
524
|
+
# Mode 2: Push message to in-memory queues. You will be responsible for clearing the
|
525
|
+
# topic queues using `Cloudenvoy::Testing.clear_all` or `Cloudenvoy::Testing.clear('my-topic')`
|
526
|
+
Cloudenvoy::Testing.fake!
|
527
|
+
```
|
528
|
+
|
529
|
+
You can query the current testing mode with:
|
530
|
+
```ruby
|
531
|
+
Cloudenvoy::Testing.enabled?
|
532
|
+
Cloudenvoy::Testing.fake?
|
533
|
+
```
|
534
|
+
|
535
|
+
Each testing mode accepts a block argument to temporarily switch to it:
|
536
|
+
```ruby
|
537
|
+
# Enable fake mode for all tests
|
538
|
+
Cloudenvoy::Testing.fake!
|
539
|
+
|
540
|
+
# Enable real mode temporarily for a given test
|
541
|
+
Cloudenvoy.enable! do
|
542
|
+
MyPublisher.publish(1,2)
|
543
|
+
end
|
544
|
+
```
|
545
|
+
|
546
|
+
Note that extension middlewares - if any has been registered - run in test mode. You can disable middlewares in your tests by adding the following to your test helper:
|
547
|
+
```ruby
|
548
|
+
# Remove all middlewares
|
549
|
+
Cloudenvoy.configure do |c|
|
550
|
+
c.publisher_middleware.clear
|
551
|
+
c.subscriber_middleware.clear
|
552
|
+
end
|
553
|
+
|
554
|
+
# Remove all specific middlewares
|
555
|
+
Cloudenvoy.configure do |c|
|
556
|
+
c.publisher_middleware.remove(MyMiddleware::Publisher)
|
557
|
+
c.subscriber_middleware.remove(MyMiddleware::Subscriber)
|
558
|
+
end
|
559
|
+
```
|
560
|
+
|
561
|
+
### In-memory queues
|
562
|
+
The `fake!` modes uses in-memory queues for topics, which can be queried and controlled using the following methods:
|
563
|
+
|
564
|
+
```ruby
|
565
|
+
# Clear all messages across all topics
|
566
|
+
Cloudenvoy::Testing.clear_all
|
567
|
+
|
568
|
+
# Remove all messages in a given topic
|
569
|
+
Cloudenvoy::Testing.clear('my-top')
|
570
|
+
|
571
|
+
# Get all messages for a given topic
|
572
|
+
Cloudenvoy::Testing.queue('my-top')
|
573
|
+
```
|
574
|
+
|
575
|
+
### Unit tests
|
576
|
+
Below are examples of rspec tests. It is assumed that `Cloudenvoy::Testing.fake!` has been set in the test helper.
|
577
|
+
|
578
|
+
**Example 1**: Testing publishers
|
579
|
+
```ruby
|
580
|
+
describe 'message publishing'
|
581
|
+
subject(:publish_message) { MyPublisher.publish(1,2) }
|
582
|
+
|
583
|
+
let(:queue) { Cloudenvoy::Testing.queue('my-topic') }
|
584
|
+
|
585
|
+
it { expect { publish_message }.to change(queue, :size).by(1) }
|
586
|
+
it { is_expected.to have_attributes(payload: { 'foo' => 'bar' }) }
|
587
|
+
end
|
588
|
+
```
|
589
|
+
|
590
|
+
**Example 2**: Testing subscribers
|
591
|
+
```ruby
|
592
|
+
describe 'message processing'
|
593
|
+
subject { VerifyDataViaApiSubscriber.new(message: message).execute } }
|
594
|
+
|
595
|
+
let(:message) { Cloudenvoy::Message.new(payload: { 'some' => 'payload' }) }
|
596
|
+
|
597
|
+
before { expect(MyApi).to receive(:fetch).and_return([]) }
|
598
|
+
it { is_expected.to be_truthy }
|
599
|
+
end
|
600
|
+
```
|
26
601
|
|
27
602
|
## Development
|
28
603
|
|
@@ -32,8 +607,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
32
607
|
|
33
608
|
## Contributing
|
34
609
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
36
|
-
|
610
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/keypup-io/cloudenvoy. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/keypup-io/cloudenvoy/blob/master/CODE_OF_CONDUCT.md).
|
37
611
|
|
38
612
|
## License
|
39
613
|
|
@@ -41,4 +615,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
41
615
|
|
42
616
|
## Code of Conduct
|
43
617
|
|
44
|
-
Everyone interacting in the Cloudenvoy project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
618
|
+
Everyone interacting in the Cloudenvoy project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/keypup-io/cloudenvoy/blob/master/CODE_OF_CONDUCT.md).
|