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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 848a9382896a69a3cb1d57a569c0fd2fc3908a3599dc5338cd26acf732ee9992
4
- data.tar.gz: 1672959f0bbae5254a6f97904db3322607080ffa7da940f1553732b883d96fcb
3
+ metadata.gz: d8f44643f00dc3a0c248fd990ffc95631a63bf9df53768ecb8e740a5dda4bc45
4
+ data.tar.gz: 92d303ac07e6402c9dc9ce4f71dcb9c56345c78980184c59692b7c7508daaf4d
5
5
  SHA512:
6
- metadata.gz: 245d52890779c61021ac9b5e3877b164e93535dae1211e351dc883c1c024bd429b1ed4f48d09401d204de16cdf971ccaba1b9daea403c43ff38e47dc874039f9
7
- data.tar.gz: a66cebab47ba27702446128492dfceac83bc6ae2382bf072547681de7989f9a98c81e2e20482d3d9fa9509c4c5d01a421b443b7349afc734b326bdfe5099327a
6
+ metadata.gz: 229de6bb9a249571e03daddb1b1288aa9138ddee9a1711538961bce90a6bc02e92ba2b83dd4f0062a78c75bb27cf92202126a40034303c10ae127bb9786eb34d
7
+ data.tar.gz: f2cd4ebef097ffc2d66fd6ec081537ae57b54c7318c132ed0cf508f26dfe03cdc2b13d3386839c209269062c344f9eb127ff8728593a7b82807db8407cb70dd2
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uscreentv/turbo-train",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "",
5
5
  "main": "turbo-train.js",
6
6
  "type": "module",
@@ -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', 'session', 'name' ];
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(`${this.href}/mercure?topic=${this.name}&authorization=${this.session}`);
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","session","name"]}constructor(){super()}connectedCallback(){this.eventSource=new EventSource(`${this.href}/mercure?topic=${this.name}&authorization=${this.session}`),e.connectStreamSource(this.eventSource)}disconnectedCallback(){e.disconnectStreamSource(this.eventSource)}get href(){return this.getAttribute("href")}get session(){return this.getAttribute("session")}get name(){return this.getAttribute("name")}};"undefined"==typeof window||window.customElements.get("turbo-train-stream-source")||window.customElements.define("turbo-train-stream-source",t);
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[:name] = Turbo::Train.signed_stream_name(streamables)
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)
@@ -1,18 +1,74 @@
1
1
  module Turbo
2
2
  module Train
3
- class Configuration
4
- attr_accessor :mercure_domain, :publisher_key, :subscriber_key, :skip_ssl_verification
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 Server
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, configuration.publisher_key, ALGORITHM
6
+ token = JWT.encode payload, server_config.publisher_key, ALGORITHM
13
7
 
14
- uri = URI("#{configuration.url}/mercure")
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
- Turbo::Train.instance_variable_set(:@server, Turbo::Train::TestServer.new(Turbo::Train.configuration))
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, Turbo::Train::Server.new(Turbo::Train.configuration))
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 < Server
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
- super
18
+ real_server.publish(topics: topics, data: data) if real_server
18
19
  end
19
20
 
20
21
  def broadcasts(channel)
@@ -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 ||= Server.new(configuration)
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)
@@ -1,5 +1,5 @@
1
1
  module Turbo
2
2
  module Train
3
- VERSION = "0.2.0"
3
+ VERSION = "0.3.0"
4
4
  end
5
5
  end
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/server'
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.2.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-15 00:00:00.000000000 Z
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/server.rb
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