journaled 3.1.0 → 4.2.0

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.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: journaled
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 4.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jake Lipson
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2021-06-02 00:00:00.000000000 Z
14
+ date: 2022-03-17 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: activejob
@@ -84,13 +84,13 @@ dependencies:
84
84
  - !ruby/object:Gem::Version
85
85
  version: '5.2'
86
86
  - !ruby/object:Gem::Dependency
87
- name: request_store
87
+ name: appraisal
88
88
  requirement: !ruby/object:Gem::Requirement
89
89
  requirements:
90
90
  - - ">="
91
91
  - !ruby/object:Gem::Version
92
92
  version: '0'
93
- type: :runtime
93
+ type: :development
94
94
  prerelease: false
95
95
  version_requirements: !ruby/object:Gem::Requirement
96
96
  requirements:
@@ -98,21 +98,21 @@ dependencies:
98
98
  - !ruby/object:Gem::Version
99
99
  version: '0'
100
100
  - !ruby/object:Gem::Dependency
101
- name: appraisal
101
+ name: betterlint
102
102
  requirement: !ruby/object:Gem::Requirement
103
103
  requirements:
104
- - - "~>"
104
+ - - ">="
105
105
  - !ruby/object:Gem::Version
106
- version: 2.2.0
106
+ version: '0'
107
107
  type: :development
108
108
  prerelease: false
109
109
  version_requirements: !ruby/object:Gem::Requirement
110
110
  requirements:
111
- - - "~>"
111
+ - - ">="
112
112
  - !ruby/object:Gem::Version
113
- version: 2.2.0
113
+ version: '0'
114
114
  - !ruby/object:Gem::Dependency
115
- name: rspec-rails
115
+ name: rspec_junit_formatter
116
116
  requirement: !ruby/object:Gem::Requirement
117
117
  requirements:
118
118
  - - ">="
@@ -126,7 +126,7 @@ dependencies:
126
126
  - !ruby/object:Gem::Version
127
127
  version: '0'
128
128
  - !ruby/object:Gem::Dependency
129
- name: rspec_junit_formatter
129
+ name: rspec-rails
130
130
  requirement: !ruby/object:Gem::Requirement
131
131
  requirements:
132
132
  - - ">="
@@ -140,21 +140,21 @@ dependencies:
140
140
  - !ruby/object:Gem::Version
141
141
  version: '0'
142
142
  - !ruby/object:Gem::Dependency
143
- name: rubocop-betterment
143
+ name: spring
144
144
  requirement: !ruby/object:Gem::Requirement
145
145
  requirements:
146
- - - "~>"
146
+ - - ">="
147
147
  - !ruby/object:Gem::Version
148
- version: '1.3'
148
+ version: '0'
149
149
  type: :development
150
150
  prerelease: false
151
151
  version_requirements: !ruby/object:Gem::Requirement
152
152
  requirements:
153
- - - "~>"
153
+ - - ">="
154
154
  - !ruby/object:Gem::Version
155
- version: '1.3'
155
+ version: '0'
156
156
  - !ruby/object:Gem::Dependency
157
- name: spring
157
+ name: spring-commands-rspec
158
158
  requirement: !ruby/object:Gem::Requirement
159
159
  requirements:
160
160
  - - ">="
@@ -168,7 +168,7 @@ dependencies:
168
168
  - !ruby/object:Gem::Version
169
169
  version: '0'
170
170
  - !ruby/object:Gem::Dependency
171
- name: spring-commands-rspec
171
+ name: sqlite3
172
172
  requirement: !ruby/object:Gem::Requirement
173
173
  requirements:
174
174
  - - ">="
@@ -182,7 +182,7 @@ dependencies:
182
182
  - !ruby/object:Gem::Version
183
183
  version: '0'
184
184
  - !ruby/object:Gem::Dependency
185
- name: sqlite3
185
+ name: timecop
186
186
  requirement: !ruby/object:Gem::Requirement
187
187
  requirements:
188
188
  - - ">="
@@ -196,7 +196,7 @@ dependencies:
196
196
  - !ruby/object:Gem::Version
197
197
  version: '0'
198
198
  - !ruby/object:Gem::Dependency
199
- name: timecop
199
+ name: uncruft
200
200
  requirement: !ruby/object:Gem::Requirement
201
201
  requirements:
202
202
  - - ">="
@@ -245,7 +245,6 @@ files:
245
245
  - app/models/journaled/change.rb
246
246
  - app/models/journaled/change_definition.rb
247
247
  - app/models/journaled/change_writer.rb
248
- - app/models/journaled/delivery.rb
249
248
  - app/models/journaled/event.rb
250
249
  - app/models/journaled/json_schema_model/validator.rb
251
250
  - app/models/journaled/not_truly_exceptional_error.rb
@@ -254,7 +253,9 @@ files:
254
253
  - config/spring.rb
255
254
  - journaled_schemas/base_event.json
256
255
  - journaled_schemas/journaled/change.json
256
+ - journaled_schemas/tagged_event.json
257
257
  - lib/journaled.rb
258
+ - lib/journaled/current.rb
258
259
  - lib/journaled/engine.rb
259
260
  - lib/journaled/relation_change_protection.rb
260
261
  - lib/journaled/rspec.rb
@@ -293,7 +294,6 @@ files:
293
294
  - spec/models/database_change_protection_spec.rb
294
295
  - spec/models/journaled/actor_uri_provider_spec.rb
295
296
  - spec/models/journaled/change_writer_spec.rb
296
- - spec/models/journaled/delivery_spec.rb
297
297
  - spec/models/journaled/event_spec.rb
298
298
  - spec/models/journaled/json_schema_model/validator_spec.rb
299
299
  - spec/models/journaled/writer_spec.rb
@@ -303,7 +303,8 @@ files:
303
303
  homepage: http://github.com/Betterment/journaled
304
304
  licenses:
305
305
  - MIT
306
- metadata: {}
306
+ metadata:
307
+ rubygems_mfa_required: 'true'
307
308
  post_install_message:
308
309
  rdoc_options: []
309
310
  require_paths:
@@ -312,14 +313,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
312
313
  requirements:
313
314
  - - ">="
314
315
  - !ruby/object:Gem::Version
315
- version: '0'
316
+ version: '2.6'
316
317
  required_rubygems_version: !ruby/object:Gem::Requirement
317
318
  requirements:
318
319
  - - ">="
319
320
  - !ruby/object:Gem::Version
320
321
  version: '0'
321
322
  requirements: []
322
- rubygems_version: 3.1.2
323
+ rubygems_version: 3.3.5
323
324
  signing_key:
324
325
  specification_version: 4
325
326
  summary: Journaling for Betterment apps.
@@ -354,7 +355,6 @@ test_files:
354
355
  - spec/dummy/README.rdoc
355
356
  - spec/models/journaled/json_schema_model/validator_spec.rb
356
357
  - spec/models/journaled/actor_uri_provider_spec.rb
357
- - spec/models/journaled/delivery_spec.rb
358
358
  - spec/models/journaled/event_spec.rb
359
359
  - spec/models/journaled/change_writer_spec.rb
360
360
  - spec/models/journaled/writer_spec.rb
@@ -1,88 +0,0 @@
1
- class Journaled::Delivery # rubocop:disable Betterment/ActiveJobPerformable
2
- DEFAULT_REGION = 'us-east-1'.freeze
3
-
4
- def initialize(serialized_event:, partition_key:, app_name:)
5
- @serialized_event = serialized_event
6
- @partition_key = partition_key
7
- @app_name = app_name
8
- end
9
-
10
- def perform
11
- kinesis_client.put_record record if Journaled.enabled?
12
- rescue Aws::Kinesis::Errors::InternalFailure, Aws::Kinesis::Errors::ServiceUnavailable, Aws::Kinesis::Errors::Http503Error => e
13
- Rails.logger.error "Kinesis Error - Server Error occurred - #{e.class}"
14
- raise KinesisTemporaryFailure
15
- rescue Seahorse::Client::NetworkingError => e
16
- Rails.logger.error "Kinesis Error - Networking Error occurred - #{e.class}"
17
- raise KinesisTemporaryFailure
18
- end
19
-
20
- def stream_name
21
- env_var_name = [app_name&.upcase, 'JOURNALED_STREAM_NAME'].compact.join('_')
22
- ENV.fetch(env_var_name)
23
- end
24
-
25
- def kinesis_client_config
26
- {
27
- region: ENV.fetch('AWS_DEFAULT_REGION', DEFAULT_REGION),
28
- retry_limit: 0,
29
- http_idle_timeout: Journaled.http_idle_timeout,
30
- http_open_timeout: Journaled.http_open_timeout,
31
- http_read_timeout: Journaled.http_read_timeout,
32
- }.merge(credentials)
33
- end
34
-
35
- private
36
-
37
- attr_reader :serialized_event, :partition_key, :app_name
38
-
39
- def record
40
- {
41
- stream_name: stream_name,
42
- data: serialized_event,
43
- partition_key: partition_key,
44
- }
45
- end
46
-
47
- def kinesis_client
48
- Aws::Kinesis::Client.new(kinesis_client_config)
49
- end
50
-
51
- def credentials
52
- if ENV.key?('JOURNALED_IAM_ROLE_ARN')
53
- {
54
- credentials: iam_assume_role_credentials,
55
- }
56
- else
57
- legacy_credentials_hash_if_present
58
- end
59
- end
60
-
61
- def legacy_credentials_hash_if_present
62
- if ENV.key?('RUBY_AWS_ACCESS_KEY_ID')
63
- {
64
- access_key_id: ENV.fetch('RUBY_AWS_ACCESS_KEY_ID'),
65
- secret_access_key: ENV.fetch('RUBY_AWS_SECRET_ACCESS_KEY'),
66
- }
67
- else
68
- {}
69
- end
70
- end
71
-
72
- def sts_client
73
- Aws::STS::Client.new({
74
- region: ENV.fetch('AWS_DEFAULT_REGION', DEFAULT_REGION),
75
- }.merge(legacy_credentials_hash_if_present))
76
- end
77
-
78
- def iam_assume_role_credentials
79
- @iam_assume_role_credentials ||= Aws::AssumeRoleCredentials.new(
80
- client: sts_client,
81
- role_arn: ENV.fetch('JOURNALED_IAM_ROLE_ARN'),
82
- role_session_name: "JournaledAssumeRoleAccess",
83
- )
84
- end
85
-
86
- class KinesisTemporaryFailure < ::Journaled::NotTrulyExceptionalError
87
- end
88
- end
@@ -1,222 +0,0 @@
1
- require 'rails_helper'
2
-
3
- RSpec.describe Journaled::Delivery do
4
- let(:stream_name) { 'test_events' }
5
- let(:partition_key) { 'fake_partition_key' }
6
- let(:serialized_event) { '{"foo":"bar"}' }
7
- let(:kinesis_client) { Aws::Kinesis::Client.new(stub_responses: true) }
8
-
9
- around do |example|
10
- with_env(JOURNALED_STREAM_NAME: stream_name) { example.run }
11
- end
12
-
13
- subject { described_class.new serialized_event: serialized_event, partition_key: partition_key, app_name: nil }
14
-
15
- describe '#perform' do
16
- let(:return_status_body) { { shard_id: '101', sequence_number: '101123' } }
17
- let(:return_object) { instance_double Aws::Kinesis::Types::PutRecordOutput, return_status_body }
18
-
19
- before do
20
- allow(Aws::AssumeRoleCredentials).to receive(:new).and_call_original
21
- allow(Aws::Kinesis::Client).to receive(:new).and_return kinesis_client
22
- kinesis_client.stub_responses(:put_record, return_status_body)
23
-
24
- allow(Journaled).to receive(:enabled?).and_return(true)
25
- end
26
-
27
- it 'makes requests to AWS to put the event on the Kinesis with the correct body' do
28
- event = subject.perform
29
-
30
- expect(event.shard_id).to eq '101'
31
- expect(event.sequence_number).to eq '101123'
32
- end
33
-
34
- context 'when JOURNALED_IAM_ROLE_ARN is defined' do
35
- let(:aws_sts_client) { Aws::STS::Client.new(stub_responses: true) }
36
-
37
- around do |example|
38
- with_env(JOURNALED_IAM_ROLE_ARN: 'iam-role-arn-for-assuming-kinesis-access') { example.run }
39
- end
40
-
41
- before do
42
- allow(Aws::STS::Client).to receive(:new).and_return aws_sts_client
43
- aws_sts_client.stub_responses(:assume_role, assume_role_response)
44
- end
45
-
46
- let(:assume_role_response) do
47
- {
48
- assumed_role_user: {
49
- arn: 'iam-role-arn-for-assuming-kinesis-access',
50
- assumed_role_id: "ARO123EXAMPLE123:Bob",
51
- },
52
- credentials: {
53
- access_key_id: "AKIAIOSFODNN7EXAMPLE",
54
- secret_access_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY",
55
- session_token: "EXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI",
56
- expiration: Time.zone.parse("2011-07-15T23:28:33.359Z"),
57
- },
58
- }
59
- end
60
-
61
- it 'initializes a Kinesis client with assume role credentials' do
62
- subject.perform
63
-
64
- expect(Aws::AssumeRoleCredentials).to have_received(:new).with(
65
- client: aws_sts_client,
66
- role_arn: "iam-role-arn-for-assuming-kinesis-access",
67
- role_session_name: "JournaledAssumeRoleAccess",
68
- )
69
- end
70
- end
71
-
72
- context 'when the stream name env var is NOT set' do
73
- let(:stream_name) { nil }
74
-
75
- it 'raises an KeyError error' do
76
- expect { subject.perform }.to raise_error KeyError
77
- end
78
- end
79
-
80
- context 'when Amazon responds with an InternalFailure' do
81
- before do
82
- kinesis_client.stub_responses(:put_record, 'InternalFailure')
83
- end
84
-
85
- it 'catches the error and re-raises a subclass of NotTrulyExceptionalError and logs about the failure' do
86
- expect(Rails.logger).to receive(:error).with("Kinesis Error - Server Error occurred - Aws::Kinesis::Errors::InternalFailure").once
87
- expect { subject.perform }.to raise_error described_class::KinesisTemporaryFailure
88
- end
89
- end
90
-
91
- context 'when Amazon responds with a ServiceUnavailable' do
92
- before do
93
- kinesis_client.stub_responses(:put_record, 'ServiceUnavailable')
94
- end
95
-
96
- it 'catches the error and re-raises a subclass of NotTrulyExceptionalError and logs about the failure' do
97
- allow(Rails.logger).to receive(:error)
98
- expect { subject.perform }.to raise_error described_class::KinesisTemporaryFailure
99
- expect(Rails.logger).to have_received(:error).with(/\AKinesis Error/).once
100
- end
101
- end
102
-
103
- context 'when we receive a 504 Gateway timeout' do
104
- before do
105
- kinesis_client.stub_responses(:put_record, 'Aws::Kinesis::Errors::ServiceError')
106
- end
107
-
108
- it 'raises an error that subclasses Aws::Kinesis::Errors::ServiceError' do
109
- expect { subject.perform }.to raise_error Aws::Kinesis::Errors::ServiceError
110
- end
111
- end
112
-
113
- context 'when the IAM user does not have permission to put_record to the specified stream' do
114
- before do
115
- kinesis_client.stub_responses(:put_record, 'AccessDeniedException')
116
- end
117
-
118
- it 'raises an AccessDeniedException error' do
119
- expect { subject.perform }.to raise_error Aws::Kinesis::Errors::AccessDeniedException
120
- end
121
- end
122
-
123
- context 'when the request timesout' do
124
- before do
125
- kinesis_client.stub_responses(:put_record, Seahorse::Client::NetworkingError.new(Timeout::Error.new))
126
- end
127
-
128
- it 'catches the error and re-raises a subclass of NotTrulyExceptionalError and logs about the failure' do
129
- expect(Rails.logger).to receive(:error).with(
130
- "Kinesis Error - Networking Error occurred - Seahorse::Client::NetworkingError",
131
- ).once
132
- expect { subject.perform }.to raise_error described_class::KinesisTemporaryFailure
133
- end
134
- end
135
- end
136
-
137
- describe "#stream_name" do
138
- context "when app_name is unspecified" do
139
- subject { described_class.new serialized_event: serialized_event, partition_key: partition_key, app_name: nil }
140
-
141
- it "is fetched from a prefixed ENV var if specified" do
142
- allow(ENV).to receive(:fetch).and_return("expected_stream_name")
143
- expect(subject.stream_name).to eq("expected_stream_name")
144
- expect(ENV).to have_received(:fetch).with("JOURNALED_STREAM_NAME")
145
- end
146
- end
147
-
148
- context "when app_name is specified" do
149
- subject { described_class.new serialized_event: serialized_event, partition_key: partition_key, app_name: "my_funky_app_name" }
150
-
151
- it "is fetched from a prefixed ENV var if specified" do
152
- allow(ENV).to receive(:fetch).and_return("expected_stream_name")
153
- expect(subject.stream_name).to eq("expected_stream_name")
154
- expect(ENV).to have_received(:fetch).with("MY_FUNKY_APP_NAME_JOURNALED_STREAM_NAME")
155
- end
156
- end
157
- end
158
-
159
- describe "#kinesis_client_config" do
160
- it "is in us-east-1 by default" do
161
- with_env(AWS_DEFAULT_REGION: nil) do
162
- expect(subject.kinesis_client_config).to include(region: 'us-east-1')
163
- end
164
- end
165
-
166
- it "respects AWS_DEFAULT_REGION env var" do
167
- with_env(AWS_DEFAULT_REGION: 'us-west-2') do
168
- expect(subject.kinesis_client_config).to include(region: 'us-west-2')
169
- end
170
- end
171
-
172
- it "doesn't limit retry" do
173
- expect(subject.kinesis_client_config).to include(retry_limit: 0)
174
- end
175
-
176
- it "provides no AWS credentials by default" do
177
- with_env(RUBY_AWS_ACCESS_KEY_ID: nil, RUBY_AWS_SECRET_ACCESS_KEY: nil) do
178
- expect(subject.kinesis_client_config).not_to have_key(:access_key_id)
179
- expect(subject.kinesis_client_config).not_to have_key(:secret_access_key)
180
- end
181
- end
182
-
183
- it "will use legacy credentials if specified" do
184
- with_env(RUBY_AWS_ACCESS_KEY_ID: 'key_id', RUBY_AWS_SECRET_ACCESS_KEY: 'secret') do
185
- expect(subject.kinesis_client_config).to include(access_key_id: 'key_id', secret_access_key: 'secret')
186
- end
187
- end
188
-
189
- it "will set http_idle_timeout by default" do
190
- expect(subject.kinesis_client_config).to include(http_idle_timeout: 5)
191
- end
192
-
193
- it "will set http_open_timeout by default" do
194
- expect(subject.kinesis_client_config).to include(http_open_timeout: 2)
195
- end
196
-
197
- it "will set http_read_timeout by default" do
198
- expect(subject.kinesis_client_config).to include(http_read_timeout: 60)
199
- end
200
-
201
- context "when Journaled.http_idle_timeout is specified" do
202
- it "will set http_idle_timeout by specified value" do
203
- allow(Journaled).to receive(:http_idle_timeout).and_return(2)
204
- expect(subject.kinesis_client_config).to include(http_idle_timeout: 2)
205
- end
206
- end
207
-
208
- context "when Journaled.http_open_timeout is specified" do
209
- it "will set http_open_timeout by specified value" do
210
- allow(Journaled).to receive(:http_open_timeout).and_return(1)
211
- expect(subject.kinesis_client_config).to include(http_open_timeout: 1)
212
- end
213
- end
214
-
215
- context "when Journaled.http_read_timeout is specified" do
216
- it "will set http_read_timeout by specified value" do
217
- allow(Journaled).to receive(:http_read_timeout).and_return(2)
218
- expect(subject.kinesis_client_config).to include(http_read_timeout: 2)
219
- end
220
- end
221
- end
222
- end