tpt-rails 1.5.3 → 1.6.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.
- checksums.yaml +4 -4
- data/README.md +30 -1
- data/app/models/tpt/rails/application_event.rb +30 -0
- data/app/models/tpt/rails/event_bridge_publisher.rb +133 -0
- data/lib/tpt/rails/version.rb +1 -1
- metadata +32 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 300608f90255d2884190ff472e70781927269af457bf549d57443edc396186ca
|
4
|
+
data.tar.gz: 5dd5a558b911dd7dae7486f0984ddeac4fa8c05d5d3407e5ef269d64fe7a3e4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 849e1766a9d4a6668516bb1d52d313e375bd823d2723668fb9917c218e79444e71400803cfecdea4f4ec7f8521832fea4c962d36af814b93fe1d4735e228d3c0
|
7
|
+
data.tar.gz: 7865a91a0e8b6553647ee022d84617aad7ef590c5e9fce5f64ea30eeeb25f7dd60ea0f71ea28702cad0f0eed20f02cd588467c657edb4610bd679437e2a63784
|
data/README.md
CHANGED
@@ -22,11 +22,13 @@ can configure this gem and enable optional features in that file.
|
|
22
22
|
See the documentation in [lib/tpt/rails.rb](lib/tpt/rails.rb).
|
23
23
|
|
24
24
|
For Kubernetes configuration:
|
25
|
+
|
25
26
|
```sh
|
26
27
|
rails generate tpt:rails:kubernetes
|
27
28
|
```
|
28
29
|
|
29
30
|
For CI/CD configuration:
|
31
|
+
|
30
32
|
```sh
|
31
33
|
rails generate tpt:rails:continuous_integration
|
32
34
|
```
|
@@ -45,6 +47,8 @@ This gem provides the following features:
|
|
45
47
|
- Adds an error test at `/internal/error-test` that reports an error and emits a metric
|
46
48
|
- Kubernetes/Helm configuration
|
47
49
|
- CI/CD configuration
|
50
|
+
- EventBridge integration
|
51
|
+
- Adds the aws and aws eventbridge gems, and the EventBridgePublisher class
|
48
52
|
|
49
53
|
See below for more details.
|
50
54
|
|
@@ -60,6 +64,29 @@ Tpt::Rails.statsd.increment('foo', tags: ['a:b', 'c:d'])
|
|
60
64
|
|
61
65
|
See the documentation in [lib/tpt/rails.rb](lib/tpt/rails.rb).
|
62
66
|
|
67
|
+
### EventBridge
|
68
|
+
|
69
|
+
`Tpt::Rails::EventBridgePublisher` is the class used to emit events to EventBridge (in batches). To use:
|
70
|
+
|
71
|
+
1. In an initializer, pass in the `Datadog::Statsd` object you're using to the class method ::set_metrics.
|
72
|
+
(Failure to do so will result in an error.) When testing, you can pass in a local Statsd, eg:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
Tpt::Rails::EventBridgePublisher.set_metrics(
|
76
|
+
Datadog::Statsd.new('localhost', 8125)
|
77
|
+
)
|
78
|
+
```
|
79
|
+
|
80
|
+
2. By default (#new with no arguments) EventBridgePublisher will try to use the following environment
|
81
|
+
variables: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `TPT_EVENT_SOURCE`, `TPT_SHARED_EVENT_BUS_NAME`.
|
82
|
+
|
83
|
+
Additional configuration can be set as part of the constructor;
|
84
|
+
[see event_bridge_publisher.rb implementation](https://github.com/TeachersPayTeachers/tpt-rails/blob/ad768d3bac0c8bd28e60a48a89aed36f01c4e17b/app/models/tpt/rails/event_bridge_publisher.rb) for more details.
|
85
|
+
|
86
|
+
3. Create events by instantiating or inheriting from `Tpt::Rails::ApplicationEvent`. Event types are derived from the text before
|
87
|
+
"Event", so ResourceEvent has the event_type "Resource". Pass in an event or an array of events to EventBridgePublisher#publish
|
88
|
+
and they will be published immediately. Large arrays will be batched (default 10 per batch).
|
89
|
+
|
63
90
|
### Error reporting (e.g. Bugsnag/Rollbar)
|
64
91
|
|
65
92
|
If you configure `rollbar_access_token` & `rollbar_enabled`, or `bugsnag_api_key` then your project
|
@@ -81,10 +108,12 @@ Tpt::Rails provides the following endpoint:
|
|
81
108
|
/internal/health-check
|
82
109
|
|
83
110
|
Two checks are added automatically:
|
111
|
+
|
84
112
|
- An ActiveRecord database check is performed if ActiveRecord is used in the app.
|
85
113
|
- A Redis check is performed if Redis is used in the app.
|
86
114
|
|
87
115
|
You may add more health checks as follows:
|
116
|
+
|
88
117
|
```ruby
|
89
118
|
config.health_check(:foo) { 1 == 1 }
|
90
119
|
```
|
@@ -132,8 +161,8 @@ gem 'tpt-rails', path: '/your/local/path/to/tpt/rails'
|
|
132
161
|
First ensure you have permissions to publish to rubygems.org.
|
133
162
|
|
134
163
|
Once you've merged, check out the `master` branch and execute:
|
164
|
+
|
135
165
|
```sh
|
136
166
|
bundle exec gem bump --push --tag --version patch # patch/minor/major/X.X.X
|
137
167
|
bundle exec gem release
|
138
168
|
```
|
139
|
-
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Tpt::Rails
|
2
|
+
class ApplicationEvent
|
3
|
+
include ActiveModel::Serializers::JSON
|
4
|
+
|
5
|
+
def initialize(data, metadata: {})
|
6
|
+
@data = data || {}
|
7
|
+
@metadata = metadata
|
8
|
+
end
|
9
|
+
|
10
|
+
def event_type
|
11
|
+
self.class.name.demodulize.gsub(/Event$/, '')
|
12
|
+
end
|
13
|
+
|
14
|
+
def ==(other)
|
15
|
+
data == other.data
|
16
|
+
end
|
17
|
+
|
18
|
+
def attributes
|
19
|
+
data.merge(metadata: metadata)
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
attr_reader :data, :metadata
|
24
|
+
|
25
|
+
private
|
26
|
+
def read_attribute_for_serialization(attr)
|
27
|
+
attributes[attr]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module Tpt::Rails
|
2
|
+
class EventBridgePublisher
|
3
|
+
def initialize(
|
4
|
+
event_bus_name: default_event_bus_name,
|
5
|
+
event_source: default_event_source,
|
6
|
+
batch_size: 10,
|
7
|
+
client_options: {}
|
8
|
+
)
|
9
|
+
@event_bus_name = event_bus_name
|
10
|
+
@event_source = event_source
|
11
|
+
@batch_size = batch_size
|
12
|
+
@client = make_client(client_options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def publish(events)
|
16
|
+
events = [events] unless events.is_a?(Array)
|
17
|
+
|
18
|
+
logger.debug "Publishing #{events.length} events to EventBridge in #{events.length/@batch_size} batch(es)"
|
19
|
+
|
20
|
+
metrics.time('tpt.event_bridge.publish') do
|
21
|
+
events.each_slice(@batch_size) do |batch|
|
22
|
+
publish_batch(batch)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
events
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
def publish_batch(events)
|
31
|
+
metrics.time('tpt.event_bridge.publish_batch') do
|
32
|
+
wrapped_events = wrap_events(events)
|
33
|
+
logger.debug({ msg: "Publishing batch of #{events.length} events to EventBridge", events: wrapped_events })
|
34
|
+
res = client.put_events(entries: wrapped_events)
|
35
|
+
handle_response(res, events)
|
36
|
+
end
|
37
|
+
rescue StandardError => e
|
38
|
+
logger.error({
|
39
|
+
msg: 'Failed to publish events',
|
40
|
+
events: events,
|
41
|
+
error: e
|
42
|
+
})
|
43
|
+
metrics.increment('tpt.event_bridge.publish_failure', by: events.length, tags: ["error_code:publish_batch_failure"])
|
44
|
+
|
45
|
+
# re-reraise the error for users to handle
|
46
|
+
raise e
|
47
|
+
end
|
48
|
+
|
49
|
+
def wrap_events(events)
|
50
|
+
events.map do |event|
|
51
|
+
{
|
52
|
+
source: event_source,
|
53
|
+
resources: [],
|
54
|
+
event_bus_name: event_bus_name,
|
55
|
+
detail: deep_camelize(event.as_json).to_json,
|
56
|
+
detail_type: event.event_type,
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def handle_response(res, events)
|
62
|
+
res.data.entries.each_with_index do |entry, index|
|
63
|
+
if entry.error_code.present?
|
64
|
+
metrics.increment('tpt.event_bridge.publish_failure', tags: ["error_code:#{entry.error_code}"])
|
65
|
+
|
66
|
+
logger.error({
|
67
|
+
msg: "Error publishing event to EventBridge",
|
68
|
+
error_code: entry.error_code,
|
69
|
+
error_message: entry.error_message,
|
70
|
+
event: events[index]
|
71
|
+
})
|
72
|
+
elsif entry.event_id.present?
|
73
|
+
metrics.increment('tpt.event_bridge.publish_success')
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
res.data.entries
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
attr_reader :client, :event_bus_name, :event_source
|
83
|
+
|
84
|
+
def make_client(options)
|
85
|
+
all_options = {
|
86
|
+
access_key_id: ENV.fetch('AWS_ACCESS_KEY_ID'),
|
87
|
+
secret_access_key: ENV.fetch('AWS_SECRET_ACCESS_KEY'),
|
88
|
+
region: ENV.fetch('AWS_REGION', 'us-east-1'),
|
89
|
+
http_open_timeout: 1, # default is 15
|
90
|
+
http_read_timeout: 1, # default is 60
|
91
|
+
}.merge(options)
|
92
|
+
|
93
|
+
Aws::EventBridge::Client.new(all_options)
|
94
|
+
end
|
95
|
+
|
96
|
+
def deep_camelize(hash)
|
97
|
+
hash.deep_transform_keys do |k|
|
98
|
+
# ignore keys like "__internal" (for now)
|
99
|
+
if k.to_s.starts_with?("_")
|
100
|
+
k
|
101
|
+
else
|
102
|
+
k.to_s.camelize(:lower)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def default_event_bus_name
|
108
|
+
ENV.fetch('TPT_SHARED_EVENT_BUS_NAME', 'shared-event-bus-dev')
|
109
|
+
end
|
110
|
+
|
111
|
+
def default_event_source
|
112
|
+
ENV.fetch('TPT_EVENT_SOURCE')
|
113
|
+
end
|
114
|
+
|
115
|
+
def logger
|
116
|
+
Rails.logger
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.set_metrics(statsd)
|
120
|
+
unless statsd.is_a?(Datadog::Statsd)
|
121
|
+
raise "argument passed to EventBridgePublisher::set_metrics is not a Datadog::Statsd class instance"
|
122
|
+
end
|
123
|
+
|
124
|
+
@@statsd = statsd
|
125
|
+
end
|
126
|
+
|
127
|
+
def metrics
|
128
|
+
raise "metrics object not set; use EventBridgePublisher#set_metrics" unless @@statsd
|
129
|
+
|
130
|
+
@@statsd
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/lib/tpt/rails/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tpt-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- TpT
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-12-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -58,6 +58,34 @@ dependencies:
|
|
58
58
|
- - "~>"
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: '0.40'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: aws-sdk-core
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 3.114.0
|
68
|
+
type: :runtime
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 3.114.0
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: aws-sdk-eventbridge
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 1.3.0
|
82
|
+
type: :runtime
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 1.3.0
|
61
89
|
- !ruby/object:Gem::Dependency
|
62
90
|
name: sqlite3
|
63
91
|
requirement: !ruby/object:Gem::Requirement
|
@@ -88,7 +116,9 @@ files:
|
|
88
116
|
- app/helpers/tpt/rails/application_helper.rb
|
89
117
|
- app/jobs/tpt/rails/application_job.rb
|
90
118
|
- app/mailers/tpt/rails/application_mailer.rb
|
119
|
+
- app/models/tpt/rails/application_event.rb
|
91
120
|
- app/models/tpt/rails/application_record.rb
|
121
|
+
- app/models/tpt/rails/event_bridge_publisher.rb
|
92
122
|
- app/views/layouts/tpt/rails/application.html.erb
|
93
123
|
- config/routes.rb
|
94
124
|
- lib/generators/tpt/rails/configuration/configuration_generator.rb
|