durable_streams-rails 0.2.5 → 0.3.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 +8 -8
- data/app/controllers/durable_streams/rails/auth_controller.rb +27 -0
- data/app/jobs/durable_streams/rails/broadcast_job.rb +13 -0
- data/app/models/concerns/durable_streams/{broadcastable.rb → rails/broadcastable.rb} +5 -5
- data/config/routes.rb +2 -2
- data/lib/durable_streams/rails/broadcastable/test_helper.rb +159 -0
- data/lib/durable_streams/{broadcasts.rb → rails/broadcasts.rb} +5 -5
- data/lib/durable_streams/rails/engine.rb +71 -0
- data/lib/durable_streams/rails/server_config.rb +64 -0
- data/lib/durable_streams/{stream_name.rb → rails/stream_name.rb} +1 -1
- data/lib/durable_streams/{testing.rb → rails/testing.rb} +2 -2
- data/lib/durable_streams/rails/version.rb +5 -0
- data/lib/durable_streams-rails.rb +7 -7
- metadata +11 -11
- data/app/controllers/durable_streams/auth_controller.rb +0 -25
- data/app/jobs/durable_streams/broadcast_job.rb +0 -9
- data/lib/durable_streams/broadcastable/test_helper.rb +0 -157
- data/lib/durable_streams/engine.rb +0 -70
- data/lib/durable_streams/server_config.rb +0 -62
- data/lib/durable_streams/version.rb +0 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5c2dc408918802eb41264671f804a2bcee440341b56b53ef2f1e58fa77b84708
|
|
4
|
+
data.tar.gz: '0496a9b62af4f8ed219f0f6d31df0da2e7f6c6b9ee871de91b11acc1cce8d707'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b4c1e809fdf66dd7b648156d5574c61f8d5ca47c365c60fd2eb4c5366fc7f87ec46bdbae0c32f848c7ef1f60863c9b96f2b5a706c9baff05f2dee082284a3e24
|
|
7
|
+
data.tar.gz: e41d5c76287f2ccbcf8a3224bb654b40087468d84796bc5c69ed9f37f7425b49a36450bf9e4bf0daec522aaeb9bff3152adc65fb531526b748296e4ea16f2a22
|
data/README.md
CHANGED
|
@@ -157,7 +157,7 @@ txid = comment.stream_upsert_to post
|
|
|
157
157
|
comment.stream_delete_to post
|
|
158
158
|
```
|
|
159
159
|
|
|
160
|
-
Asynchronous (via `DurableStreams::BroadcastJob`):
|
|
160
|
+
Asynchronous (via `DurableStreams::Rails::BroadcastJob`):
|
|
161
161
|
|
|
162
162
|
```ruby
|
|
163
163
|
comment.stream_insert_later_to post
|
|
@@ -210,7 +210,7 @@ end
|
|
|
210
210
|
The gem provides test helpers that mirror Turbo's `assert_turbo_stream_broadcasts`. They are
|
|
211
211
|
automatically included in `ActiveSupport::TestCase` via the engine.
|
|
212
212
|
|
|
213
|
-
During tests, `DurableStreams::Testing` intercepts all broadcasts at the transport layer —
|
|
213
|
+
During tests, `DurableStreams::Rails::Testing` intercepts all broadcasts at the transport layer —
|
|
214
214
|
events are captured in-memory instead of being sent to the stream server. This means tests
|
|
215
215
|
verify the Rails integration (DSL wiring, event shape, callbacks, jobs, suppression) without
|
|
216
216
|
requiring a running Durable Streams server. Same pattern as Turbo, where `ActionCable::TestHelper`
|
|
@@ -249,13 +249,13 @@ This gem mirrors `turbo-rails` 1:1 in structure and design:
|
|
|
249
249
|
|
|
250
250
|
| Turbo Rails | Durable Streams Rails | Role |
|
|
251
251
|
|---|---|---|
|
|
252
|
-
| `Turbo::Streams::StreamName` | `DurableStreams::StreamName` | Name generation (private `stream_name_from`) |
|
|
253
|
-
| `Turbo::Streams::Broadcasts` | `DurableStreams::Broadcasts` | Flatten/compact guards, serialization, transport |
|
|
252
|
+
| `Turbo::Streams::StreamName` | `DurableStreams::Rails::StreamName` | Name generation (private `stream_name_from`) |
|
|
253
|
+
| `Turbo::Streams::Broadcasts` | `DurableStreams::Rails::Broadcasts` | Flatten/compact guards, serialization, transport |
|
|
254
254
|
| `Turbo::StreamsChannel` | `DurableStreams` module | Entry point (extends StreamName + Broadcasts) |
|
|
255
|
-
| `Turbo::Broadcastable` | `DurableStreams::Broadcastable` | Model concern, delegates to module |
|
|
256
|
-
| `Turbo::Streams::ActionBroadcastJob` | `DurableStreams::BroadcastJob` | Async job, delegates to module |
|
|
257
|
-
| `ActionCable::TestHelper` | `DurableStreams::Testing` | Test transport interception |
|
|
258
|
-
| `Turbo::Broadcastable::TestHelper` | `DurableStreams::Broadcastable::TestHelper` | Test assertions |
|
|
255
|
+
| `Turbo::Broadcastable` | `DurableStreams::Rails::Broadcastable` | Model concern, delegates to module |
|
|
256
|
+
| `Turbo::Streams::ActionBroadcastJob` | `DurableStreams::Rails::BroadcastJob` | Async job, delegates to module |
|
|
257
|
+
| `ActionCable::TestHelper` | `DurableStreams::Rails::Testing` | Test transport interception |
|
|
258
|
+
| `Turbo::Broadcastable::TestHelper` | `DurableStreams::Rails::Broadcastable::TestHelper` | Test assertions |
|
|
259
259
|
|
|
260
260
|
The key architectural difference: Turbo broadcasts HTML fragments over WebSockets (Action Cable).
|
|
261
261
|
This gem broadcasts JSON State Protocol events over HTTP/SSE (Durable Streams).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module DurableStreams
|
|
2
|
+
module Rails
|
|
3
|
+
class AuthController < ActionController::API
|
|
4
|
+
def verify
|
|
5
|
+
if request.headers["X-Forwarded-Uri"]
|
|
6
|
+
stream_name ? head(:ok) : head(:unauthorized)
|
|
7
|
+
elsif valid_server_api_key?
|
|
8
|
+
head :ok
|
|
9
|
+
else
|
|
10
|
+
head :unauthorized
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
def stream_name
|
|
16
|
+
DurableStreams.verify_signed_url(request.headers["X-Forwarded-Uri"])
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def valid_server_api_key?
|
|
20
|
+
if auth_header = request.headers["Authorization"]
|
|
21
|
+
auth_header.start_with?("Bearer ") &&
|
|
22
|
+
ActiveSupport::SecurityUtils.secure_compare(auth_header.delete_prefix("Bearer "), DurableStreams.server_api_key)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# The job that powers all the <tt>stream_*_later_to</tt> broadcasts available in <tt>DurableStreams::Rails::Broadcastable</tt>.
|
|
2
|
+
module DurableStreams
|
|
3
|
+
module Rails
|
|
4
|
+
class BroadcastJob < ActiveJob::Base
|
|
5
|
+
discard_on ActiveJob::DeserializationError
|
|
6
|
+
retry_on DurableStreams::ConnectionError, wait: :polynomially_longer, attempts: 10
|
|
7
|
+
|
|
8
|
+
def perform(stream_name, type:, key:, value:, operation:)
|
|
9
|
+
DurableStreams.broadcast_event_to stream_name, type: type, key: key, value: value, operation: operation
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
# Comment.suppressing_streams do
|
|
48
48
|
# Comment.create!(post: post) # This won't broadcast the insert event
|
|
49
49
|
# end
|
|
50
|
-
module DurableStreams::Broadcastable
|
|
50
|
+
module DurableStreams::Rails::Broadcastable
|
|
51
51
|
extend ActiveSupport::Concern
|
|
52
52
|
|
|
53
53
|
included do
|
|
@@ -155,7 +155,7 @@ module DurableStreams::Broadcastable
|
|
|
155
155
|
stream_delete_to self
|
|
156
156
|
end
|
|
157
157
|
|
|
158
|
-
# Same as <tt>#stream_insert_to</tt> but run asynchronously via a <tt>DurableStreams::BroadcastJob</tt>.
|
|
158
|
+
# Same as <tt>#stream_insert_to</tt> but run asynchronously via a <tt>DurableStreams::Rails::BroadcastJob</tt>.
|
|
159
159
|
def stream_insert_later_to(*streamables)
|
|
160
160
|
DurableStreams.broadcast_event_later_to(*streamables, **stream_event_attributes(operation: :insert)) unless suppressed_streams?
|
|
161
161
|
end
|
|
@@ -165,7 +165,7 @@ module DurableStreams::Broadcastable
|
|
|
165
165
|
stream_insert_later_to self
|
|
166
166
|
end
|
|
167
167
|
|
|
168
|
-
# Same as <tt>#stream_update_to</tt> but run asynchronously via a <tt>DurableStreams::BroadcastJob</tt>.
|
|
168
|
+
# Same as <tt>#stream_update_to</tt> but run asynchronously via a <tt>DurableStreams::Rails::BroadcastJob</tt>.
|
|
169
169
|
def stream_update_later_to(*streamables)
|
|
170
170
|
DurableStreams.broadcast_event_later_to(*streamables, **stream_event_attributes(operation: :update)) unless suppressed_streams?
|
|
171
171
|
end
|
|
@@ -175,7 +175,7 @@ module DurableStreams::Broadcastable
|
|
|
175
175
|
stream_update_later_to self
|
|
176
176
|
end
|
|
177
177
|
|
|
178
|
-
# Same as <tt>#stream_upsert_to</tt> but run asynchronously via a <tt>DurableStreams::BroadcastJob</tt>.
|
|
178
|
+
# Same as <tt>#stream_upsert_to</tt> but run asynchronously via a <tt>DurableStreams::Rails::BroadcastJob</tt>.
|
|
179
179
|
def stream_upsert_later_to(*streamables)
|
|
180
180
|
DurableStreams.broadcast_event_later_to(*streamables, **stream_event_attributes(operation: :upsert)) unless suppressed_streams?
|
|
181
181
|
end
|
|
@@ -185,7 +185,7 @@ module DurableStreams::Broadcastable
|
|
|
185
185
|
stream_upsert_later_to self
|
|
186
186
|
end
|
|
187
187
|
|
|
188
|
-
# Same as <tt>#stream_delete_to</tt> but run asynchronously via a <tt>DurableStreams::BroadcastJob</tt>.
|
|
188
|
+
# Same as <tt>#stream_delete_to</tt> but run asynchronously via a <tt>DurableStreams::Rails::BroadcastJob</tt>.
|
|
189
189
|
def stream_delete_later_to(*streamables)
|
|
190
190
|
DurableStreams.broadcast_event_later_to(*streamables, **stream_event_attributes(operation: :delete, value: nil)) unless suppressed_streams?
|
|
191
191
|
end
|
data/config/routes.rb
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
Rails.application.routes.draw do
|
|
2
|
-
get "durable_streams/auth/verify", to: "durable_streams/auth#verify", as: :durable_streams_auth_verify
|
|
1
|
+
::Rails.application.routes.draw do
|
|
2
|
+
get "durable_streams/auth/verify", to: "durable_streams/rails/auth#verify", as: :durable_streams_auth_verify
|
|
3
3
|
end if DurableStreams.draw_routes
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
module DurableStreams
|
|
2
|
+
module Rails
|
|
3
|
+
module Broadcastable
|
|
4
|
+
module TestHelper
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
include DurableStreams::Rails::StreamName
|
|
9
|
+
|
|
10
|
+
setup { DurableStreams::Rails::Testing.install! }
|
|
11
|
+
teardown { DurableStreams::Rails::Testing.reset! }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Asserts that State Protocol events were broadcast to a stream
|
|
15
|
+
#
|
|
16
|
+
# ==== Arguments
|
|
17
|
+
#
|
|
18
|
+
# * <tt>stream_name_or_object</tt> the objects used to generate the
|
|
19
|
+
# stream name, or the name itself
|
|
20
|
+
# * <tt>&block</tt> optional block executed before the
|
|
21
|
+
# assertion
|
|
22
|
+
#
|
|
23
|
+
# ==== Options
|
|
24
|
+
#
|
|
25
|
+
# * <tt>count:</tt> the number of events that are expected to be broadcast
|
|
26
|
+
#
|
|
27
|
+
# Asserts events were broadcast:
|
|
28
|
+
#
|
|
29
|
+
# comment = Comment.find(1)
|
|
30
|
+
# comment.stream_insert_to comment.post
|
|
31
|
+
#
|
|
32
|
+
# assert_stream_broadcasts comment.post
|
|
33
|
+
#
|
|
34
|
+
# Asserts that two events were broadcast:
|
|
35
|
+
#
|
|
36
|
+
# comment = Comment.find(1)
|
|
37
|
+
# comment.stream_insert_to comment.post
|
|
38
|
+
# comment.stream_update_to comment.post
|
|
39
|
+
#
|
|
40
|
+
# assert_stream_broadcasts comment.post, count: 2
|
|
41
|
+
#
|
|
42
|
+
# You can pass a block to run before the assertion:
|
|
43
|
+
#
|
|
44
|
+
# comment = Comment.find(1)
|
|
45
|
+
#
|
|
46
|
+
# assert_stream_broadcasts comment.post do
|
|
47
|
+
# comment.stream_insert_to comment.post
|
|
48
|
+
# end
|
|
49
|
+
#
|
|
50
|
+
# In addition to a String, the helper also accepts an Object or Array to
|
|
51
|
+
# determine the stream name:
|
|
52
|
+
#
|
|
53
|
+
# post = Post.find(1)
|
|
54
|
+
#
|
|
55
|
+
# assert_stream_broadcasts post do
|
|
56
|
+
# post.comments.create!(body: "Hello")
|
|
57
|
+
# end
|
|
58
|
+
#
|
|
59
|
+
def assert_stream_broadcasts(stream_name_or_object, count: nil, &block)
|
|
60
|
+
payloads = capture_stream_broadcasts(stream_name_or_object, &block)
|
|
61
|
+
stream_name = stream_name_from(stream_name_or_object)
|
|
62
|
+
|
|
63
|
+
if count.nil?
|
|
64
|
+
assert_not_empty payloads, "Expected at least one broadcast on #{stream_name.inspect}, but there were none"
|
|
65
|
+
else
|
|
66
|
+
broadcasts = "Durable Stream broadcast".pluralize(count)
|
|
67
|
+
|
|
68
|
+
assert count == payloads.count, "Expected #{count} #{broadcasts} on #{stream_name.inspect}, but there were #{payloads.count}"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Asserts that no State Protocol events were broadcast to a stream
|
|
73
|
+
#
|
|
74
|
+
# ==== Arguments
|
|
75
|
+
#
|
|
76
|
+
# * <tt>stream_name_or_object</tt> the objects used to generate the
|
|
77
|
+
# stream name, or the name itself
|
|
78
|
+
# * <tt>&block</tt> optional block executed before the
|
|
79
|
+
# assertion
|
|
80
|
+
#
|
|
81
|
+
# Asserts that no events were broadcast:
|
|
82
|
+
#
|
|
83
|
+
# assert_no_stream_broadcasts "messages" do
|
|
84
|
+
# # do something other than broadcast to "messages"
|
|
85
|
+
# end
|
|
86
|
+
#
|
|
87
|
+
# In addition to a String, the helper also accepts an Object or Array to
|
|
88
|
+
# determine the stream name:
|
|
89
|
+
#
|
|
90
|
+
# post = Post.find(1)
|
|
91
|
+
#
|
|
92
|
+
# assert_no_stream_broadcasts post do
|
|
93
|
+
# # do something other than broadcast to post's stream
|
|
94
|
+
# end
|
|
95
|
+
#
|
|
96
|
+
def assert_no_stream_broadcasts(stream_name_or_object, &block)
|
|
97
|
+
block&.call
|
|
98
|
+
|
|
99
|
+
stream_name = stream_name_from(stream_name_or_object)
|
|
100
|
+
payloads = stream_broadcasts_for(stream_name)
|
|
101
|
+
|
|
102
|
+
assert payloads.empty?, "Expected no broadcasts on #{stream_name.inspect}, but there were #{payloads.count}"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Captures any State Protocol events that were broadcast to a stream
|
|
106
|
+
#
|
|
107
|
+
# ==== Arguments
|
|
108
|
+
#
|
|
109
|
+
# * <tt>stream_name_or_object</tt> the objects used to generate the
|
|
110
|
+
# stream name, or the name itself
|
|
111
|
+
# * <tt>&block</tt> optional block to capture broadcasts during execution
|
|
112
|
+
#
|
|
113
|
+
# Returns any events that have been broadcast as an Array of parsed JSON hashes
|
|
114
|
+
#
|
|
115
|
+
# comment = Comment.find(1)
|
|
116
|
+
# comment.stream_insert_to comment.post
|
|
117
|
+
# comment.stream_update_to comment.post
|
|
118
|
+
#
|
|
119
|
+
# events = capture_stream_broadcasts comment.post
|
|
120
|
+
#
|
|
121
|
+
# assert_equal "insert", events.first["headers"]["operation"]
|
|
122
|
+
# assert_equal "update", events.second["headers"]["operation"]
|
|
123
|
+
#
|
|
124
|
+
# You can pass a block to limit the scope of the broadcasts being captured:
|
|
125
|
+
#
|
|
126
|
+
# comment = Comment.find(1)
|
|
127
|
+
#
|
|
128
|
+
# events = capture_stream_broadcasts comment.post do
|
|
129
|
+
# comment.stream_insert_to comment.post
|
|
130
|
+
# end
|
|
131
|
+
#
|
|
132
|
+
# assert_equal "insert", events.first["headers"]["operation"]
|
|
133
|
+
#
|
|
134
|
+
def capture_stream_broadcasts(stream_name_or_object, &block)
|
|
135
|
+
stream_name = stream_name_from(stream_name_or_object)
|
|
136
|
+
|
|
137
|
+
if block_given?
|
|
138
|
+
before = stream_broadcasts_for(stream_name).size
|
|
139
|
+
block.call
|
|
140
|
+
stream_broadcasts_for(stream_name)[before..]
|
|
141
|
+
else
|
|
142
|
+
stream_broadcasts_for(stream_name)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
private
|
|
147
|
+
def stream_broadcasts_for(stream_name)
|
|
148
|
+
DurableStreams::Rails::Testing.messages_for(stream_name).map do |message|
|
|
149
|
+
if message.is_a?(String)
|
|
150
|
+
JSON.parse(message)
|
|
151
|
+
else
|
|
152
|
+
message
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Provides the broadcast actions in synchronous and asynchronous form for the <tt>DurableStreams</tt> module.
|
|
2
|
-
# See <tt>DurableStreams::Broadcastable</tt> for the user-facing API that invokes these methods with most of the
|
|
2
|
+
# See <tt>DurableStreams::Rails::Broadcastable</tt> for the user-facing API that invokes these methods with most of the
|
|
3
3
|
# paperwork filled out already.
|
|
4
4
|
#
|
|
5
5
|
# Can be used directly using something like <tt>DurableStreams.broadcast_event_to :room, type: "message", key: "5",
|
|
6
6
|
# value: { content: "Hello" }, operation: :insert</tt>.
|
|
7
|
-
module DurableStreams::Broadcasts
|
|
7
|
+
module DurableStreams::Rails::Broadcasts
|
|
8
8
|
def broadcast_event_to(*streamables, type:, key:, value:, operation:)
|
|
9
9
|
streamables.flatten!
|
|
10
10
|
streamables.compact_blank!
|
|
@@ -29,7 +29,7 @@ module DurableStreams::Broadcasts
|
|
|
29
29
|
streamables.compact_blank!
|
|
30
30
|
|
|
31
31
|
if streamables.present?
|
|
32
|
-
DurableStreams::BroadcastJob.perform_later \
|
|
32
|
+
DurableStreams::Rails::BroadcastJob.perform_later \
|
|
33
33
|
stream_name_from(streamables),
|
|
34
34
|
type: type, key: key, value: value, operation: operation.to_s
|
|
35
35
|
end
|
|
@@ -46,8 +46,8 @@ module DurableStreams::Broadcasts
|
|
|
46
46
|
|
|
47
47
|
private
|
|
48
48
|
def append_to_stream(stream_name, message)
|
|
49
|
-
if DurableStreams::Testing.recording?
|
|
50
|
-
DurableStreams::Testing.record(stream_name, message)
|
|
49
|
+
if DurableStreams::Rails::Testing.recording?
|
|
50
|
+
DurableStreams::Rails::Testing.record(stream_name, message)
|
|
51
51
|
else
|
|
52
52
|
stream(stream_name).append(message)
|
|
53
53
|
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require "rails/engine"
|
|
2
|
+
|
|
3
|
+
module DurableStreams
|
|
4
|
+
module Rails
|
|
5
|
+
class Engine < ::Rails::Engine
|
|
6
|
+
isolate_namespace DurableStreams::Rails
|
|
7
|
+
config.durable_streams = ActiveSupport::OrderedOptions.new
|
|
8
|
+
config.autoload_once_paths = %W[
|
|
9
|
+
#{root}/app/controllers
|
|
10
|
+
#{root}/app/models
|
|
11
|
+
#{root}/app/models/concerns
|
|
12
|
+
#{root}/app/jobs
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
# If the parent application does not use Active Job, app/jobs cannot
|
|
16
|
+
# be eager loaded, because it references the ActiveJob constant.
|
|
17
|
+
initializer "durable_streams_rails.no_active_job", before: :set_eager_load_paths do
|
|
18
|
+
unless defined?(ActiveJob)
|
|
19
|
+
::Rails.autoloaders.once.do_not_eager_load("#{root}/app/jobs")
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
initializer "durable_streams_rails.broadcastable" do
|
|
24
|
+
ActiveSupport.on_load(:active_record) do
|
|
25
|
+
if defined?(ActiveJob)
|
|
26
|
+
include DurableStreams::Rails::Broadcastable
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
initializer "durable_streams_rails.configs" do
|
|
32
|
+
config.after_initialize do |app|
|
|
33
|
+
DurableStreams.draw_routes = app.config.durable_streams.draw_routes != false
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
initializer "durable_streams_rails.signed_stream_verifier_key" do
|
|
38
|
+
config.after_initialize do
|
|
39
|
+
DurableStreams.signed_stream_verifier_key =
|
|
40
|
+
config.durable_streams.signed_stream_verifier_key ||
|
|
41
|
+
::Rails.application.key_generator.generate_key("durable_streams/signed_stream_verifier_key")
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
initializer "durable_streams_rails.server_api_key" do
|
|
46
|
+
config.after_initialize do
|
|
47
|
+
DurableStreams.server_api_key =
|
|
48
|
+
config.durable_streams.server_api_key ||
|
|
49
|
+
::Rails.application.key_generator.generate_key("durable_streams/server_api_key").unpack1("H*")
|
|
50
|
+
|
|
51
|
+
if DurableStreams.base_url.present?
|
|
52
|
+
DurableStreams.configure do |c|
|
|
53
|
+
c.base_url = DurableStreams.base_url
|
|
54
|
+
c.default_headers = { "Authorization" => "Bearer #{DurableStreams.server_api_key}" }
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# No Action Cable dependency -- load test helpers directly.
|
|
61
|
+
initializer "durable_streams_rails.test_assertions" do
|
|
62
|
+
ActiveSupport.on_load(:active_support_test_case) do
|
|
63
|
+
if defined?(ActiveJob)
|
|
64
|
+
require "durable_streams/rails/broadcastable/test_helper"
|
|
65
|
+
include DurableStreams::Rails::Broadcastable::TestHelper
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require "yaml"
|
|
2
|
+
require "erb"
|
|
3
|
+
|
|
4
|
+
module DurableStreams
|
|
5
|
+
module Rails
|
|
6
|
+
class ServerConfig
|
|
7
|
+
TEMPLATE = ERB.new(<<~'CADDYFILE', trim_mode: "-")
|
|
8
|
+
{
|
|
9
|
+
admin off
|
|
10
|
+
<%- unless @config["domain"] -%>
|
|
11
|
+
auto_https off
|
|
12
|
+
<%- end -%>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
<%= address %> {
|
|
16
|
+
route <%= route_path %> {
|
|
17
|
+
forward_auth <%= auth["url"] %> {
|
|
18
|
+
uri <%= auth["path"] %>
|
|
19
|
+
<%- copy_headers.each do |header| -%>
|
|
20
|
+
copy_headers <%= header %>
|
|
21
|
+
<%- end -%>
|
|
22
|
+
}
|
|
23
|
+
durable_streams<%= storage_block %>
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
CADDYFILE
|
|
27
|
+
|
|
28
|
+
def self.generate(config_path, environment)
|
|
29
|
+
new(config_path, environment).generate
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def initialize(config_path, environment)
|
|
33
|
+
@config = YAML.load_file(config_path, aliases: true).fetch(environment)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def generate
|
|
37
|
+
TEMPLATE.result(binding)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
def address
|
|
42
|
+
@config["domain"] || ":#{@config.fetch("port", 4437)}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def route_path
|
|
46
|
+
@config.fetch("route", "/v1/streams/*")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def auth
|
|
50
|
+
@config.fetch("auth")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def copy_headers
|
|
54
|
+
auth.fetch("copy_headers", [ "Cookie" ])
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def storage_block
|
|
58
|
+
if dir = @config.dig("storage", "data_dir")
|
|
59
|
+
" {\n\t\t\tdata_dir #{dir}\n\t\t}"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# are exposed directly to the client via signed URL tokens, we need to ensure that the name isn't
|
|
3
3
|
# tampered with, so the names are signed upon generation and verified upon receipt. All verification
|
|
4
4
|
# happens through the <tt>DurableStreams.signed_stream_verifier</tt>.
|
|
5
|
-
module DurableStreams::StreamName
|
|
5
|
+
module DurableStreams::Rails::StreamName
|
|
6
6
|
# Used by the stream auth endpoint to verify a signed stream name.
|
|
7
7
|
def verified_stream_name(signed_stream_name)
|
|
8
8
|
DurableStreams.signed_stream_verifier.verified signed_stream_name
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
require "active_support/core_ext/module/attribute_accessors_per_thread"
|
|
2
2
|
|
|
3
3
|
# Intercepts stream appends during tests, similar to how <tt>ActionCable::TestHelper</tt> captures broadcasts.
|
|
4
|
-
# Activated by <tt>DurableStreams::Broadcastable::TestHelper</tt> via +setup+/+teardown+ hooks.
|
|
4
|
+
# Activated by <tt>DurableStreams::Rails::Broadcastable::TestHelper</tt> via +setup+/+teardown+ hooks.
|
|
5
5
|
#
|
|
6
6
|
# When installed, all calls to <tt>DurableStreams.broadcast_event_to</tt> and <tt>DurableStreams.broadcast_to</tt>
|
|
7
7
|
# are recorded in memory instead of being sent to the stream server. Captured messages can be inspected via
|
|
8
8
|
# <tt>messages_for</tt>.
|
|
9
|
-
module DurableStreams::Testing
|
|
9
|
+
module DurableStreams::Rails::Testing
|
|
10
10
|
thread_mattr_accessor :messages
|
|
11
11
|
|
|
12
12
|
class << self
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
require "durable_streams"
|
|
2
|
-
require "durable_streams/stream_name"
|
|
3
|
-
require "durable_streams/broadcasts"
|
|
4
|
-
require "durable_streams/testing"
|
|
5
|
-
require "durable_streams/engine"
|
|
2
|
+
require "durable_streams/rails/stream_name"
|
|
3
|
+
require "durable_streams/rails/broadcasts"
|
|
4
|
+
require "durable_streams/rails/testing"
|
|
5
|
+
require "durable_streams/rails/engine"
|
|
6
6
|
require "active_support/core_ext/module/attribute_accessors_per_thread"
|
|
7
7
|
|
|
8
8
|
module DurableStreams
|
|
9
9
|
extend ActiveSupport::Autoload
|
|
10
|
-
extend DurableStreams::StreamName
|
|
11
|
-
extend DurableStreams::Broadcasts
|
|
10
|
+
extend DurableStreams::Rails::StreamName
|
|
11
|
+
extend DurableStreams::Rails::Broadcasts
|
|
12
12
|
|
|
13
13
|
mattr_accessor :base_url
|
|
14
14
|
mattr_accessor :draw_routes, default: true
|
|
@@ -55,7 +55,7 @@ module DurableStreams
|
|
|
55
55
|
# Idempotent — the server returns 200 when the stream already exists with matching config.
|
|
56
56
|
# Skipped during tests (no server) and when base_url is not configured.
|
|
57
57
|
def ensure_stream_exists(stream_name)
|
|
58
|
-
return if DurableStreams::Testing.recording?
|
|
58
|
+
return if DurableStreams::Rails::Testing.recording?
|
|
59
59
|
|
|
60
60
|
stream(stream_name).create_stream(content_type: "application/json")
|
|
61
61
|
rescue DurableStreams::StreamExistsError
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: durable_streams-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- tokimonki
|
|
@@ -48,18 +48,18 @@ files:
|
|
|
48
48
|
- MIT-LICENSE
|
|
49
49
|
- README.md
|
|
50
50
|
- Rakefile
|
|
51
|
-
- app/controllers/durable_streams/auth_controller.rb
|
|
52
|
-
- app/jobs/durable_streams/broadcast_job.rb
|
|
53
|
-
- app/models/concerns/durable_streams/broadcastable.rb
|
|
51
|
+
- app/controllers/durable_streams/rails/auth_controller.rb
|
|
52
|
+
- app/jobs/durable_streams/rails/broadcast_job.rb
|
|
53
|
+
- app/models/concerns/durable_streams/rails/broadcastable.rb
|
|
54
54
|
- config/routes.rb
|
|
55
55
|
- lib/durable_streams-rails.rb
|
|
56
|
-
- lib/durable_streams/broadcastable/test_helper.rb
|
|
57
|
-
- lib/durable_streams/broadcasts.rb
|
|
58
|
-
- lib/durable_streams/engine.rb
|
|
59
|
-
- lib/durable_streams/server_config.rb
|
|
60
|
-
- lib/durable_streams/stream_name.rb
|
|
61
|
-
- lib/durable_streams/testing.rb
|
|
62
|
-
- lib/durable_streams/version.rb
|
|
56
|
+
- lib/durable_streams/rails/broadcastable/test_helper.rb
|
|
57
|
+
- lib/durable_streams/rails/broadcasts.rb
|
|
58
|
+
- lib/durable_streams/rails/engine.rb
|
|
59
|
+
- lib/durable_streams/rails/server_config.rb
|
|
60
|
+
- lib/durable_streams/rails/stream_name.rb
|
|
61
|
+
- lib/durable_streams/rails/testing.rb
|
|
62
|
+
- lib/durable_streams/rails/version.rb
|
|
63
63
|
- lib/generators/durable_streams/install/USAGE
|
|
64
64
|
- lib/generators/durable_streams/install/install_generator.rb
|
|
65
65
|
- lib/generators/durable_streams/install/templates/bin/durable-streams.tt
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
module DurableStreams
|
|
2
|
-
class AuthController < ActionController::API
|
|
3
|
-
def verify
|
|
4
|
-
if request.headers["X-Forwarded-Uri"]
|
|
5
|
-
stream_name ? head(:ok) : head(:unauthorized)
|
|
6
|
-
elsif valid_server_api_key?
|
|
7
|
-
head :ok
|
|
8
|
-
else
|
|
9
|
-
head :unauthorized
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
private
|
|
14
|
-
def stream_name
|
|
15
|
-
DurableStreams.verify_signed_url(request.headers["X-Forwarded-Uri"])
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def valid_server_api_key?
|
|
19
|
-
if auth_header = request.headers["Authorization"]
|
|
20
|
-
auth_header.start_with?("Bearer ") &&
|
|
21
|
-
ActiveSupport::SecurityUtils.secure_compare(auth_header.delete_prefix("Bearer "), DurableStreams.server_api_key)
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# The job that powers all the <tt>stream_*_later_to</tt> broadcasts available in <tt>DurableStreams::Broadcastable</tt>.
|
|
2
|
-
class DurableStreams::BroadcastJob < ActiveJob::Base
|
|
3
|
-
discard_on ActiveJob::DeserializationError
|
|
4
|
-
retry_on DurableStreams::ConnectionError, wait: :polynomially_longer, attempts: 10
|
|
5
|
-
|
|
6
|
-
def perform(stream_name, type:, key:, value:, operation:)
|
|
7
|
-
DurableStreams.broadcast_event_to stream_name, type: type, key: key, value: value, operation: operation
|
|
8
|
-
end
|
|
9
|
-
end
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
module DurableStreams
|
|
2
|
-
module Broadcastable
|
|
3
|
-
module TestHelper
|
|
4
|
-
extend ActiveSupport::Concern
|
|
5
|
-
|
|
6
|
-
included do
|
|
7
|
-
include DurableStreams::StreamName
|
|
8
|
-
|
|
9
|
-
setup { DurableStreams::Testing.install! }
|
|
10
|
-
teardown { DurableStreams::Testing.reset! }
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# Asserts that State Protocol events were broadcast to a stream
|
|
14
|
-
#
|
|
15
|
-
# ==== Arguments
|
|
16
|
-
#
|
|
17
|
-
# * <tt>stream_name_or_object</tt> the objects used to generate the
|
|
18
|
-
# stream name, or the name itself
|
|
19
|
-
# * <tt>&block</tt> optional block executed before the
|
|
20
|
-
# assertion
|
|
21
|
-
#
|
|
22
|
-
# ==== Options
|
|
23
|
-
#
|
|
24
|
-
# * <tt>count:</tt> the number of events that are expected to be broadcast
|
|
25
|
-
#
|
|
26
|
-
# Asserts events were broadcast:
|
|
27
|
-
#
|
|
28
|
-
# comment = Comment.find(1)
|
|
29
|
-
# comment.stream_insert_to comment.post
|
|
30
|
-
#
|
|
31
|
-
# assert_stream_broadcasts comment.post
|
|
32
|
-
#
|
|
33
|
-
# Asserts that two events were broadcast:
|
|
34
|
-
#
|
|
35
|
-
# comment = Comment.find(1)
|
|
36
|
-
# comment.stream_insert_to comment.post
|
|
37
|
-
# comment.stream_update_to comment.post
|
|
38
|
-
#
|
|
39
|
-
# assert_stream_broadcasts comment.post, count: 2
|
|
40
|
-
#
|
|
41
|
-
# You can pass a block to run before the assertion:
|
|
42
|
-
#
|
|
43
|
-
# comment = Comment.find(1)
|
|
44
|
-
#
|
|
45
|
-
# assert_stream_broadcasts comment.post do
|
|
46
|
-
# comment.stream_insert_to comment.post
|
|
47
|
-
# end
|
|
48
|
-
#
|
|
49
|
-
# In addition to a String, the helper also accepts an Object or Array to
|
|
50
|
-
# determine the stream name:
|
|
51
|
-
#
|
|
52
|
-
# post = Post.find(1)
|
|
53
|
-
#
|
|
54
|
-
# assert_stream_broadcasts post do
|
|
55
|
-
# post.comments.create!(body: "Hello")
|
|
56
|
-
# end
|
|
57
|
-
#
|
|
58
|
-
def assert_stream_broadcasts(stream_name_or_object, count: nil, &block)
|
|
59
|
-
payloads = capture_stream_broadcasts(stream_name_or_object, &block)
|
|
60
|
-
stream_name = stream_name_from(stream_name_or_object)
|
|
61
|
-
|
|
62
|
-
if count.nil?
|
|
63
|
-
assert_not_empty payloads, "Expected at least one broadcast on #{stream_name.inspect}, but there were none"
|
|
64
|
-
else
|
|
65
|
-
broadcasts = "Durable Stream broadcast".pluralize(count)
|
|
66
|
-
|
|
67
|
-
assert count == payloads.count, "Expected #{count} #{broadcasts} on #{stream_name.inspect}, but there were #{payloads.count}"
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# Asserts that no State Protocol events were broadcast to a stream
|
|
72
|
-
#
|
|
73
|
-
# ==== Arguments
|
|
74
|
-
#
|
|
75
|
-
# * <tt>stream_name_or_object</tt> the objects used to generate the
|
|
76
|
-
# stream name, or the name itself
|
|
77
|
-
# * <tt>&block</tt> optional block executed before the
|
|
78
|
-
# assertion
|
|
79
|
-
#
|
|
80
|
-
# Asserts that no events were broadcast:
|
|
81
|
-
#
|
|
82
|
-
# assert_no_stream_broadcasts "messages" do
|
|
83
|
-
# # do something other than broadcast to "messages"
|
|
84
|
-
# end
|
|
85
|
-
#
|
|
86
|
-
# In addition to a String, the helper also accepts an Object or Array to
|
|
87
|
-
# determine the stream name:
|
|
88
|
-
#
|
|
89
|
-
# post = Post.find(1)
|
|
90
|
-
#
|
|
91
|
-
# assert_no_stream_broadcasts post do
|
|
92
|
-
# # do something other than broadcast to post's stream
|
|
93
|
-
# end
|
|
94
|
-
#
|
|
95
|
-
def assert_no_stream_broadcasts(stream_name_or_object, &block)
|
|
96
|
-
block&.call
|
|
97
|
-
|
|
98
|
-
stream_name = stream_name_from(stream_name_or_object)
|
|
99
|
-
payloads = stream_broadcasts_for(stream_name)
|
|
100
|
-
|
|
101
|
-
assert payloads.empty?, "Expected no broadcasts on #{stream_name.inspect}, but there were #{payloads.count}"
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
# Captures any State Protocol events that were broadcast to a stream
|
|
105
|
-
#
|
|
106
|
-
# ==== Arguments
|
|
107
|
-
#
|
|
108
|
-
# * <tt>stream_name_or_object</tt> the objects used to generate the
|
|
109
|
-
# stream name, or the name itself
|
|
110
|
-
# * <tt>&block</tt> optional block to capture broadcasts during execution
|
|
111
|
-
#
|
|
112
|
-
# Returns any events that have been broadcast as an Array of parsed JSON hashes
|
|
113
|
-
#
|
|
114
|
-
# comment = Comment.find(1)
|
|
115
|
-
# comment.stream_insert_to comment.post
|
|
116
|
-
# comment.stream_update_to comment.post
|
|
117
|
-
#
|
|
118
|
-
# events = capture_stream_broadcasts comment.post
|
|
119
|
-
#
|
|
120
|
-
# assert_equal "insert", events.first["headers"]["operation"]
|
|
121
|
-
# assert_equal "update", events.second["headers"]["operation"]
|
|
122
|
-
#
|
|
123
|
-
# You can pass a block to limit the scope of the broadcasts being captured:
|
|
124
|
-
#
|
|
125
|
-
# comment = Comment.find(1)
|
|
126
|
-
#
|
|
127
|
-
# events = capture_stream_broadcasts comment.post do
|
|
128
|
-
# comment.stream_insert_to comment.post
|
|
129
|
-
# end
|
|
130
|
-
#
|
|
131
|
-
# assert_equal "insert", events.first["headers"]["operation"]
|
|
132
|
-
#
|
|
133
|
-
def capture_stream_broadcasts(stream_name_or_object, &block)
|
|
134
|
-
stream_name = stream_name_from(stream_name_or_object)
|
|
135
|
-
|
|
136
|
-
if block_given?
|
|
137
|
-
before = stream_broadcasts_for(stream_name).size
|
|
138
|
-
block.call
|
|
139
|
-
stream_broadcasts_for(stream_name)[before..]
|
|
140
|
-
else
|
|
141
|
-
stream_broadcasts_for(stream_name)
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
private
|
|
146
|
-
def stream_broadcasts_for(stream_name)
|
|
147
|
-
DurableStreams::Testing.messages_for(stream_name).map do |message|
|
|
148
|
-
if message.is_a?(String)
|
|
149
|
-
JSON.parse(message)
|
|
150
|
-
else
|
|
151
|
-
message
|
|
152
|
-
end
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
end
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
require "rails/engine"
|
|
2
|
-
|
|
3
|
-
module DurableStreams
|
|
4
|
-
class Engine < Rails::Engine
|
|
5
|
-
isolate_namespace DurableStreams
|
|
6
|
-
config.eager_load_namespaces << DurableStreams
|
|
7
|
-
config.durable_streams = ActiveSupport::OrderedOptions.new
|
|
8
|
-
config.autoload_once_paths = %W(
|
|
9
|
-
#{root}/app/controllers
|
|
10
|
-
#{root}/app/models
|
|
11
|
-
#{root}/app/models/concerns
|
|
12
|
-
#{root}/app/jobs
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
# If the parent application does not use Active Job, app/jobs cannot
|
|
16
|
-
# be eager loaded, because it references the ActiveJob constant.
|
|
17
|
-
initializer "durable_streams.no_active_job", before: :set_eager_load_paths do
|
|
18
|
-
unless defined?(ActiveJob)
|
|
19
|
-
Rails.autoloaders.once.do_not_eager_load("#{root}/app/jobs")
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
initializer "durable_streams.broadcastable" do
|
|
24
|
-
ActiveSupport.on_load(:active_record) do
|
|
25
|
-
if defined?(ActiveJob)
|
|
26
|
-
include DurableStreams::Broadcastable
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
initializer "durable_streams.configs" do
|
|
32
|
-
config.after_initialize do |app|
|
|
33
|
-
DurableStreams.draw_routes = app.config.durable_streams.draw_routes != false
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
initializer "durable_streams.signed_stream_verifier_key" do
|
|
38
|
-
config.after_initialize do
|
|
39
|
-
DurableStreams.signed_stream_verifier_key =
|
|
40
|
-
config.durable_streams.signed_stream_verifier_key ||
|
|
41
|
-
Rails.application.key_generator.generate_key("durable_streams/signed_stream_verifier_key")
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
initializer "durable_streams.server_api_key" do
|
|
46
|
-
config.after_initialize do
|
|
47
|
-
DurableStreams.server_api_key =
|
|
48
|
-
config.durable_streams.server_api_key ||
|
|
49
|
-
Rails.application.key_generator.generate_key("durable_streams/server_api_key").unpack1("H*")
|
|
50
|
-
|
|
51
|
-
if DurableStreams.base_url.present?
|
|
52
|
-
DurableStreams.configure do |c|
|
|
53
|
-
c.base_url = DurableStreams.base_url
|
|
54
|
-
c.default_headers = { "Authorization" => "Bearer #{DurableStreams.server_api_key}" }
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# No Action Cable dependency -- load test helpers directly.
|
|
61
|
-
initializer "durable_streams.test_assertions" do
|
|
62
|
-
ActiveSupport.on_load(:active_support_test_case) do
|
|
63
|
-
if defined?(ActiveJob)
|
|
64
|
-
require "durable_streams/broadcastable/test_helper"
|
|
65
|
-
include DurableStreams::Broadcastable::TestHelper
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
require "yaml"
|
|
2
|
-
require "erb"
|
|
3
|
-
|
|
4
|
-
module DurableStreams
|
|
5
|
-
class ServerConfig
|
|
6
|
-
TEMPLATE = ERB.new(<<~'CADDYFILE', trim_mode: "-")
|
|
7
|
-
{
|
|
8
|
-
admin off
|
|
9
|
-
<%- unless @config["domain"] -%>
|
|
10
|
-
auto_https off
|
|
11
|
-
<%- end -%>
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
<%= address %> {
|
|
15
|
-
route <%= route_path %> {
|
|
16
|
-
forward_auth <%= auth["url"] %> {
|
|
17
|
-
uri <%= auth["path"] %>
|
|
18
|
-
<%- copy_headers.each do |header| -%>
|
|
19
|
-
copy_headers <%= header %>
|
|
20
|
-
<%- end -%>
|
|
21
|
-
}
|
|
22
|
-
durable_streams<%= storage_block %>
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
CADDYFILE
|
|
26
|
-
|
|
27
|
-
def self.generate(config_path, environment)
|
|
28
|
-
new(config_path, environment).generate
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def initialize(config_path, environment)
|
|
32
|
-
@config = YAML.load_file(config_path, aliases: true).fetch(environment)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def generate
|
|
36
|
-
TEMPLATE.result(binding)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
private
|
|
40
|
-
def address
|
|
41
|
-
@config["domain"] || ":#{@config.fetch("port", 4437)}"
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def route_path
|
|
45
|
-
@config.fetch("route", "/v1/streams/*")
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def auth
|
|
49
|
-
@config.fetch("auth")
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def copy_headers
|
|
53
|
-
auth.fetch("copy_headers", [ "Cookie" ])
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def storage_block
|
|
57
|
-
if dir = @config.dig("storage", "data_dir")
|
|
58
|
-
" {\n\t\t\tdata_dir #{dir}\n\t\t}"
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|