turbo-train 0.2.0 → 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/app/assets/javascripts/package.json +1 -1
- data/app/assets/javascripts/turbo-train.js +2 -10
- data/app/assets/javascripts/turbo-train.min.js +1 -1
- data/app/helpers/turbo/train/streams_helper.rb +1 -3
- data/lib/turbo/train/base_server.rb +27 -0
- data/lib/turbo/train/broadcasts.rb +3 -3
- data/lib/turbo/train/config.rb +59 -3
- data/lib/turbo/train/fanout_server.rb +36 -0
- data/lib/turbo/train/{server.rb → mercure_server.rb} +7 -9
- data/lib/turbo/train/test_helper.rb +40 -2
- data/lib/turbo/train/test_server.rb +5 -4
- data/lib/turbo/train/train.rb +21 -6
- data/lib/turbo/train/version.rb +1 -1
- data/lib/turbo/train.rb +3 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8f44643f00dc3a0c248fd990ffc95631a63bf9df53768ecb8e740a5dda4bc45
|
4
|
+
data.tar.gz: 92d303ac07e6402c9dc9ce4f71dcb9c56345c78980184c59692b7c7508daaf4d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 229de6bb9a249571e03daddb1b1288aa9138ddee9a1711538961bce90a6bc02e92ba2b83dd4f0062a78c75bb27cf92202126a40034303c10ae127bb9786eb34d
|
7
|
+
data.tar.gz: f2cd4ebef097ffc2d66fd6ec081537ae57b54c7318c132ed0cf508f26dfe03cdc2b13d3386839c209269062c344f9eb127ff8728593a7b82807db8407cb70dd2
|
@@ -2,7 +2,7 @@ import { Turbo } from "@hotwired/turbo-rails"
|
|
2
2
|
|
3
3
|
export default class TurboTrain extends HTMLElement {
|
4
4
|
static get observedAttributes() {
|
5
|
-
return [ 'href'
|
5
|
+
return [ 'href' ];
|
6
6
|
}
|
7
7
|
|
8
8
|
constructor() {
|
@@ -10,7 +10,7 @@ export default class TurboTrain extends HTMLElement {
|
|
10
10
|
}
|
11
11
|
|
12
12
|
connectedCallback() {
|
13
|
-
this.eventSource = new EventSource(
|
13
|
+
this.eventSource = new EventSource(this.href);
|
14
14
|
Turbo.connectStreamSource(this.eventSource);
|
15
15
|
}
|
16
16
|
|
@@ -21,14 +21,6 @@ export default class TurboTrain extends HTMLElement {
|
|
21
21
|
get href() {
|
22
22
|
return this.getAttribute('href');
|
23
23
|
}
|
24
|
-
|
25
|
-
get session() {
|
26
|
-
return this.getAttribute('session');
|
27
|
-
}
|
28
|
-
|
29
|
-
get name() {
|
30
|
-
return this.getAttribute('name');
|
31
|
-
}
|
32
24
|
}
|
33
25
|
|
34
26
|
if (
|
@@ -1 +1 @@
|
|
1
|
-
import{Turbo as e}from"@hotwired/turbo-rails";export default class t extends HTMLElement{static get observedAttributes(){return["href"
|
1
|
+
import{Turbo as e}from"@hotwired/turbo-rails";export default class t extends HTMLElement{static get observedAttributes(){return["href"]}constructor(){super()}connectedCallback(){this.eventSource=new EventSource(this.href),e.connectStreamSource(this.eventSource)}disconnectedCallback(){e.disconnectStreamSource(this.eventSource)}get href(){return this.getAttribute("href")}};"undefined"==typeof window||window.customElements.get("turbo-train-stream-source")||window.customElements.define("turbo-train-stream-source",t);
|
@@ -1,8 +1,6 @@
|
|
1
1
|
module Turbo::Train::StreamsHelper
|
2
2
|
def turbo_train_from(*streamables, **attributes)
|
3
|
-
attributes[:
|
4
|
-
attributes[:session] = Turbo::Train.encode({ platform: "web" })
|
5
|
-
attributes[:href] = Turbo::Train.configuration.url
|
3
|
+
attributes[:href] = Turbo::Train.server(attributes[:server]&.to_sym).listen_url(streamables, platform: "web")
|
6
4
|
tag.turbo_train_stream_source(**attributes)
|
7
5
|
end
|
8
6
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Turbo
|
2
|
+
module Train
|
3
|
+
class BaseServer
|
4
|
+
attr_reader :configuration
|
5
|
+
|
6
|
+
def initialize(configuration)
|
7
|
+
@configuration = configuration
|
8
|
+
end
|
9
|
+
|
10
|
+
def publish(topics:, data:)
|
11
|
+
raise NotImplementedError
|
12
|
+
end
|
13
|
+
|
14
|
+
def server_config
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
|
18
|
+
def publish_url
|
19
|
+
server_config.publish_url
|
20
|
+
end
|
21
|
+
|
22
|
+
def listen_url(topic, **options)
|
23
|
+
server_config.listen_url(topic, **options)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Turbo::Train::Broadcasts
|
2
|
-
def broadcast(streamables, content:)
|
2
|
+
def broadcast(streamables, content:, server: nil)
|
3
3
|
topics = if streamables.is_a?(Array)
|
4
4
|
streamables.map { |s| signed_stream_name(s) }
|
5
5
|
else
|
@@ -11,13 +11,13 @@ module Turbo::Train::Broadcasts
|
|
11
11
|
data: content
|
12
12
|
}
|
13
13
|
|
14
|
-
Turbo::Train.server.publish(topics: topics, data: data)
|
14
|
+
Turbo::Train.server(server).publish(topics: topics, data: data)
|
15
15
|
end
|
16
16
|
|
17
17
|
def broadcast_action_to(*streamables, action:, target: nil, targets: nil, **rendering)
|
18
18
|
broadcast(streamables, content: turbo_stream_action_tag(action, target: target, targets: targets, template:
|
19
19
|
rendering.delete(:content) || rendering.delete(:html) || (rendering.any? ? render_format(:html, **rendering) : nil)
|
20
|
-
))
|
20
|
+
), server: rendering.delete(:server))
|
21
21
|
end
|
22
22
|
|
23
23
|
def broadcast_render_to(*streamables, **rendering)
|
data/lib/turbo/train/config.rb
CHANGED
@@ -1,18 +1,74 @@
|
|
1
1
|
module Turbo
|
2
2
|
module Train
|
3
|
-
class
|
4
|
-
attr_accessor :mercure_domain, :publisher_key, :subscriber_key
|
3
|
+
class MercureConfiguration
|
4
|
+
attr_accessor :mercure_domain, :publisher_key, :subscriber_key
|
5
5
|
|
6
6
|
def initialize
|
7
|
+
super
|
7
8
|
@mercure_domain = 'localhost'
|
8
9
|
@publisher_key = 'test'
|
9
10
|
@subscriber_key = 'testing'
|
10
|
-
@skip_ssl_verification = Rails.env.development? || Rails.env.test?
|
11
11
|
end
|
12
12
|
|
13
13
|
def url
|
14
14
|
"https://#{mercure_domain}/.well-known"
|
15
15
|
end
|
16
|
+
|
17
|
+
def publish_url
|
18
|
+
"#{url}/mercure"
|
19
|
+
end
|
20
|
+
|
21
|
+
def listen_url(topic, platform: 'web')
|
22
|
+
"#{url}/mercure?topic=#{Turbo::Train.signed_stream_name(topic)}&authorization=#{jwt_auth_token({ platform: platform })}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def jwt_auth_token(payload)
|
26
|
+
structured_payload = { mercure: { payload: payload } }
|
27
|
+
JWT.encode structured_payload, subscriber_key, ALGORITHM
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class FanoutConfiguration
|
32
|
+
attr_accessor :fastly_api_url, :service_url, :fastly_key, :service_id
|
33
|
+
|
34
|
+
def initialize
|
35
|
+
super
|
36
|
+
@fastly_api_url = 'https://api.fastly.com'
|
37
|
+
@service_url = 'https://johnny-cage-fake-url.edgecompute.app'
|
38
|
+
@fastly_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
|
39
|
+
end
|
40
|
+
|
41
|
+
def publish_url
|
42
|
+
"#{@fastly_api_url}/service/#{@service_id}/publish/"
|
43
|
+
end
|
44
|
+
|
45
|
+
def listen_url(topic, **)
|
46
|
+
"#{service_url}/stream/sse?topic=#{Turbo::Train.signed_stream_name(topic)}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Configuration
|
51
|
+
attr_accessor :skip_ssl_verification, :mercure, :fanout, :default_server
|
52
|
+
|
53
|
+
def initialize
|
54
|
+
@skip_ssl_verification = Rails.env.development? || Rails.env.test?
|
55
|
+
@mercure = nil
|
56
|
+
@fanout = nil
|
57
|
+
@default_server = :mercure
|
58
|
+
end
|
59
|
+
|
60
|
+
def server(server_name)
|
61
|
+
case server_name
|
62
|
+
when :mercure
|
63
|
+
@mercure ||= MercureConfiguration.new
|
64
|
+
yield(@mercure)
|
65
|
+
when :fanout
|
66
|
+
@fanout ||= FanoutConfiguration.new
|
67
|
+
yield(@fanout)
|
68
|
+
else
|
69
|
+
raise ArgumentError, "Unknown server name: #{server_name}"
|
70
|
+
end
|
71
|
+
end
|
16
72
|
end
|
17
73
|
|
18
74
|
class << self
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Turbo
|
2
|
+
module Train
|
3
|
+
class FanoutServer < BaseServer
|
4
|
+
def publish(topics:, data:)
|
5
|
+
uri = URI(server_config.publish_url)
|
6
|
+
req = Net::HTTP::Post.new(uri)
|
7
|
+
req['Fastly-Key'] = server_config.fastly_key
|
8
|
+
|
9
|
+
message = data[:data].gsub("\n", "")
|
10
|
+
payload = {items: []}
|
11
|
+
|
12
|
+
Array(topics).each do |topic|
|
13
|
+
payload[:items] << {channel: topic, formats: { 'http-stream': { content: "event: message\ndata: #{message}\n\n" } } }
|
14
|
+
end
|
15
|
+
|
16
|
+
req.body = payload.to_json
|
17
|
+
|
18
|
+
opts = {
|
19
|
+
use_ssl: uri.scheme == 'https'
|
20
|
+
}
|
21
|
+
|
22
|
+
if configuration.skip_ssl_verification
|
23
|
+
opts[:verify_mode] = OpenSSL::SSL::VERIFY_NONE
|
24
|
+
end
|
25
|
+
|
26
|
+
Net::HTTP.start(uri.host, uri.port, opts) do |http|
|
27
|
+
http.request(req)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def server_config
|
32
|
+
configuration.fanout
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,17 +1,11 @@
|
|
1
1
|
module Turbo
|
2
2
|
module Train
|
3
|
-
class
|
4
|
-
attr_reader :configuration
|
5
|
-
|
6
|
-
def initialize(configuration)
|
7
|
-
@configuration = configuration
|
8
|
-
end
|
9
|
-
|
3
|
+
class MercureServer < BaseServer
|
10
4
|
def publish(topics:, data:)
|
11
5
|
payload = { mercure: { publish: topics } }
|
12
|
-
token = JWT.encode payload,
|
6
|
+
token = JWT.encode payload, server_config.publisher_key, ALGORITHM
|
13
7
|
|
14
|
-
uri = URI(
|
8
|
+
uri = URI(publish_url)
|
15
9
|
|
16
10
|
req = Net::HTTP::Post.new(uri)
|
17
11
|
req['Content-Type'] = 'application/x-www-form-urlencoded'
|
@@ -30,6 +24,10 @@ module Turbo
|
|
30
24
|
http.request(req)
|
31
25
|
end
|
32
26
|
end
|
27
|
+
|
28
|
+
def server_config
|
29
|
+
configuration.mercure
|
30
|
+
end
|
33
31
|
end
|
34
32
|
end
|
35
33
|
end
|
@@ -3,13 +3,31 @@ module Turbo
|
|
3
3
|
module Train
|
4
4
|
module TestHelper
|
5
5
|
def before_setup
|
6
|
-
|
6
|
+
test_server = case ENV.fetch('TURBO_TRAIN_TEST_SERVER', 'mercure').to_sym
|
7
|
+
when :mercure
|
8
|
+
Turbo::Train::TestServer.new(Turbo::Train.mercure_server, Turbo::Train.configuration)
|
9
|
+
when :fanout
|
10
|
+
Turbo::Train::TestServer.new(Turbo::Train.fanout_server, Turbo::Train.configuration)
|
11
|
+
else
|
12
|
+
raise "Unknown test server: #{ENV['TURBO_TRAIN_TEST_SERVER']}"
|
13
|
+
end
|
14
|
+
|
15
|
+
Turbo::Train.instance_variable_set(:@server, test_server)
|
7
16
|
super
|
8
17
|
end
|
9
18
|
|
10
19
|
def after_teardown
|
20
|
+
test_server = case ENV.fetch('TURBO_TRAIN_TEST_SERVER', 'mercure').to_sym
|
21
|
+
when :mercure
|
22
|
+
Turbo::Train.mercure_server
|
23
|
+
when :fanout
|
24
|
+
Turbo::Train.fanout_server
|
25
|
+
else
|
26
|
+
raise "Unknown test server: #{ENV['TURBO_TRAIN_TEST_SERVER']}"
|
27
|
+
end
|
28
|
+
|
11
29
|
super
|
12
|
-
Turbo::Train.instance_variable_set(:@server,
|
30
|
+
Turbo::Train.instance_variable_set(:@server, test_server)
|
13
31
|
end
|
14
32
|
|
15
33
|
def assert_broadcast_on(stream, data, &block)
|
@@ -36,6 +54,26 @@ module Turbo
|
|
36
54
|
|
37
55
|
assert message, "No messages sent with #{data} to #{Turbo::Train.stream_name_from(stream)}"
|
38
56
|
end
|
57
|
+
|
58
|
+
def assert_body_match(r)
|
59
|
+
if Turbo::Train.server.real_server.is_a?(Turbo::Train::FanoutServer)
|
60
|
+
assert_match "Published\n", r.body
|
61
|
+
elsif Turbo::Train.server.real_server.is_a?(Turbo::Train::MercureServer)
|
62
|
+
assert_match /urn:uuid:.*/, r.body
|
63
|
+
else
|
64
|
+
raise "Unknown server type"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def assert_response_from_mercure_server(r)
|
69
|
+
assert_equal r.code, '200'
|
70
|
+
assert_match /urn:uuid:.*/, r.body
|
71
|
+
end
|
72
|
+
|
73
|
+
def assert_response_from_fanout_server(r)
|
74
|
+
assert_equal r.code, '200'
|
75
|
+
assert_match "Published\n", r.body
|
76
|
+
end
|
39
77
|
end
|
40
78
|
end
|
41
79
|
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
module Turbo
|
2
2
|
module Train
|
3
|
-
class TestServer <
|
4
|
-
attr_reader :configuration, :channels_data
|
3
|
+
class TestServer < BaseServer
|
4
|
+
attr_reader :configuration, :channels_data, :real_server
|
5
5
|
|
6
|
-
def initialize(configuration)
|
6
|
+
def initialize(real_server, configuration)
|
7
7
|
@configuration = configuration
|
8
8
|
@channels_data = {}
|
9
|
+
@real_server = real_server
|
9
10
|
end
|
10
11
|
|
11
12
|
def publish(topics:, data:)
|
@@ -14,7 +15,7 @@ module Turbo
|
|
14
15
|
@channels_data[topic] << data[:data]
|
15
16
|
end
|
16
17
|
|
17
|
-
|
18
|
+
real_server.publish(topics: topics, data: data) if real_server
|
18
19
|
end
|
19
20
|
|
20
21
|
def broadcasts(channel)
|
data/lib/turbo/train/train.rb
CHANGED
@@ -18,8 +18,27 @@ module Turbo
|
|
18
18
|
Turbo.signed_stream_verifier.generate stream_name_from(streamables)
|
19
19
|
end
|
20
20
|
|
21
|
-
def server
|
22
|
-
@server ||=
|
21
|
+
def server(server = nil)
|
22
|
+
@server ||= case server || configuration.default_server
|
23
|
+
when :mercure
|
24
|
+
mercure_server
|
25
|
+
when :fanout
|
26
|
+
fanout_server
|
27
|
+
else
|
28
|
+
raise ArgumentError, "Unknown server: #{server}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def mercure_server
|
33
|
+
raise ArgumentError, "Mercure configuration is missing" unless configuration.mercure
|
34
|
+
|
35
|
+
@mercure_server ||= MercureServer.new(configuration)
|
36
|
+
end
|
37
|
+
|
38
|
+
def fanout_server
|
39
|
+
raise ArgumentError, "Fanout configuration is missing" unless configuration.fanout
|
40
|
+
|
41
|
+
@fanout_server ||= FanoutServer.new(configuration)
|
23
42
|
end
|
24
43
|
|
25
44
|
def stream_name_from(streamables)
|
@@ -30,10 +49,6 @@ module Turbo
|
|
30
49
|
end
|
31
50
|
end
|
32
51
|
|
33
|
-
def url
|
34
|
-
configuration.url
|
35
|
-
end
|
36
|
-
|
37
52
|
private
|
38
53
|
|
39
54
|
def render_format(format, **rendering)
|
data/lib/turbo/train/version.rb
CHANGED
data/lib/turbo/train.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require "turbo/train/version"
|
2
2
|
require "turbo/train/config"
|
3
3
|
require 'turbo/train/broadcasts'
|
4
|
-
require 'turbo/train/
|
4
|
+
require 'turbo/train/base_server'
|
5
|
+
require 'turbo/train/mercure_server'
|
6
|
+
require 'turbo/train/fanout_server'
|
5
7
|
require 'turbo/train/test_server'
|
6
8
|
require 'turbo/train/test_helper'
|
7
9
|
require "turbo/train/engine"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: turbo-train
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Savrov
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-05-
|
12
|
+
date: 2023-05-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -84,10 +84,12 @@ files:
|
|
84
84
|
- lib/install/install_node.rb
|
85
85
|
- lib/tasks/install_tasks.rake
|
86
86
|
- lib/turbo/train.rb
|
87
|
+
- lib/turbo/train/base_server.rb
|
87
88
|
- lib/turbo/train/broadcasts.rb
|
88
89
|
- lib/turbo/train/config.rb
|
89
90
|
- lib/turbo/train/engine.rb
|
90
|
-
- lib/turbo/train/
|
91
|
+
- lib/turbo/train/fanout_server.rb
|
92
|
+
- lib/turbo/train/mercure_server.rb
|
91
93
|
- lib/turbo/train/test_helper.rb
|
92
94
|
- lib/turbo/train/test_server.rb
|
93
95
|
- lib/turbo/train/train.rb
|