shared_broker 1.1.1 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 545d29807ba6d68209d43f93f98b81b0e30532efe6d799d5df4f59650e8c44f8
4
- data.tar.gz: 5eb9ac3cf999257d3c9f94db10140aee626ed42c1658dd727ca4e2b05f25eee9
3
+ metadata.gz: d47439f3383e9d40d13664b67dc8b77318b64f628e6a1efa0f4684130ff2ff4b
4
+ data.tar.gz: 85d081f58ac1608a1629b29f14c634b370b963c65338eb7f9a32ace26bc90502
5
5
  SHA512:
6
- metadata.gz: 64d138f86ba1e88e86b2a65224df35946972843c928a73531b92b00e21bda3ab102a45b32e51db09a5c1f1d1ef7a6b9983e04978c5597408f74537ce01cbec1d
7
- data.tar.gz: 7239a6c03ae9b78a8682e579a7d6becdd3c3edc782f8df587d13440d26f3f96e3695c96768890cb110372c3e6819f81bbd40ea90f628c21d03427743275dabb3
6
+ metadata.gz: 25151e9182b26d17334f04b121831703eee6c8b95981ac2b235085472501f06ec97c2a6b14cedc434dd3ee49963577b8eceedc175680525025d31e3246ac81dd
7
+ data.tar.gz: 07cb36e946dd24260fdd70d7cd995f4aeae8167b764d1df18e2026bc523481ec4d788308f720494472985a3b1a4adc7ae8751ded5abbd20b02c8c475c10a97c6
data/CHANGELOG.md CHANGED
@@ -5,25 +5,35 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [1.1.0] - Unreleased
8
+ ## [1.2.0] - 2026-06-07
9
9
 
10
10
  ### Added
11
- - Phase 4: Pipeline of customizable Middlewares (Interceptors) for publishing and subscribing messages.
12
- - Phase 4: Distributed Tracing using W3C Trace Context (`traceparent`/`tracestate`) injection and extraction.
11
+ - Pipeline of customizable Middlewares (Interceptors) for publishing and subscribing messages.
12
+ - Distributed Tracing using W3C Trace Context (`traceparent`/`tracestate`) context injection and extraction.
13
+
14
+ ## [1.1.1] - 2026-06-06
15
+
16
+ ### Changed
17
+ - Restructured `README.md` to clearly separate minimum required configuration from optional/advanced setups.
18
+
19
+ ## [1.1.0] - 2026-06-06
20
+
21
+ ### Added
22
+ - Scalable Adapters:
23
+ - Apache Kafka adapter (`SharedBroker::Adapters::Kafka`) with dynamic dependency loading.
24
+ - Redis Pub/Sub adapter (`SharedBroker::Adapters::Redis`) with list-based DLQ routing.
25
+ - Mock test configurations for Kafka and Redis to run adapter unit tests.
13
26
 
14
27
  ## [1.0.0] - 2026-06-06
15
28
 
16
29
  ### Added
17
- - Phase 1: Fault Tolerance & Resilience features:
30
+ - Fault Tolerance & Resilience features:
18
31
  - Exponential backoff retry loop for message processing.
19
32
  - Automatic Dead Letter Queue (DLQ) routing with rich metadata headers (`x_failed_at`, `x_exception_class`, `x_exception_message`, `x_original_routing_key`).
20
33
  - Thread-safe `CircuitBreaker` wrapping all outbound publisher calls.
21
- - Phase 2: Schema Validation and Security:
34
+ - Schema Validation and Security:
22
35
  - Outbound/Inbound boundary validation using `dry-schema`.
23
36
  - Transparent AES-256-GCM symmetric payload encryption by default, configurable with `SharedBroker.encryption_key`.
24
- - Phase 3: Scalable Adapters:
25
- - Apache Kafka adapter (`SharedBroker::Adapters::Kafka`) with dynamic dependency loading.
26
- - Redis Pub/Sub adapter (`SharedBroker::Adapters::Redis`) with list-based DLQ routing.
27
37
  - Comprehensive test coverage for all the above features using isolated fakes and Minitest.
28
38
 
29
39
  ## [0.1.0] - 2026-06-06
data/README.md CHANGED
@@ -73,7 +73,7 @@ SPOT_BROKER = SharedBroker::Client.new(adapter: BROKER_ADAPTER)
73
73
 
74
74
  ---
75
75
 
76
- ### 2. Optional Configuration (Advanced Features)
76
+ ### 2. Optional Configuration
77
77
 
78
78
  These features can be configured optionally depending on your needs.
79
79
 
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SharedBroker
4
+ class MiddlewarePipeline
5
+ def initialize(middlewares)
6
+ @middlewares = Array(middlewares)
7
+ end
8
+
9
+ def execute(topic, message, metadata = {}, &block)
10
+ run_middleware(0, topic, message, metadata, &block)
11
+ end
12
+
13
+ private
14
+
15
+ def run_middleware(index, topic, message, metadata, &block)
16
+ if index >= @middlewares.size
17
+ yield
18
+ else
19
+ @middlewares[index].call(topic, message, metadata) do
20
+ run_middleware(index + 1, topic, message, metadata, &block)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "opentelemetry"
4
+
5
+ module SharedBroker
6
+ module Middlewares
7
+ class OpenTelemetryPropagation
8
+ def call(topic, message, metadata)
9
+ if metadata[:operation] == :publish
10
+ carrier = {}
11
+ ::OpenTelemetry.propagation.inject(carrier)
12
+ carrier.each do |key, value|
13
+ message["_#{key}".to_sym] = value
14
+ end
15
+ yield
16
+ elsif metadata[:operation] == :subscribe
17
+ carrier = {}
18
+ message.each do |key, value|
19
+ if key.to_s.start_with?("_trace")
20
+ carrier[key.to_s.sub(/^_/, "")] = value
21
+ end
22
+ end
23
+
24
+ parent_context = ::OpenTelemetry.propagation.extract(carrier)
25
+ ::OpenTelemetry::Context.with_current(parent_context) do
26
+ tracer = ::OpenTelemetry.tracer_provider.tracer("shared_broker")
27
+ tracer.in_span("#{topic} process", kind: :consumer) do |span|
28
+ span.set_attribute("messaging.system", "shared_broker")
29
+ span.set_attribute("messaging.destination", topic)
30
+ yield
31
+ end
32
+ end
33
+ else
34
+ yield
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SharedBroker
4
- VERSION = "1.1.1"
4
+ VERSION = "1.2.0"
5
5
  end
data/lib/shared_broker.rb CHANGED
@@ -1,51 +1,63 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "shared_broker/version"
4
- require_relative "shared_broker/telemetry"
5
- require_relative "shared_broker/circuit_breaker"
6
- require_relative "shared_broker/validation"
7
- require_relative "shared_broker/cipher"
8
- require_relative "shared_broker/adapters/base"
9
- require_relative "shared_broker/adapters/in_memory"
10
- require_relative "shared_broker/adapters/rabbit_mq"
11
- require_relative "shared_broker/adapters/kafka"
12
- require_relative "shared_broker/adapters/redis"
13
-
14
- module SharedBroker
15
- class << self
16
- attr_accessor :encryption_key
17
- end
18
-
19
- # Default key for development/test if not set
20
- @encryption_key = ENV.fetch("SHARED_BROKER_ENCRYPTION_KEY") { "a" * 32 }
21
-
22
- class Client
23
- attr_reader :circuit_breaker
24
-
25
- def initialize(adapter:, circuit_breaker: nil)
26
- unless adapter.respond_to?(:publish) && adapter.respond_to?(:subscribe)
27
- raise ArgumentError, "Expected adapter to respond to :publish and :subscribe, got #{adapter.class} with value #{adapter.inspect}"
28
- end
29
-
30
- @adapter = adapter
31
- @circuit_breaker = circuit_breaker || CircuitBreaker.new
32
- end
33
-
34
- def publish(topic, message, correlation_id: nil)
35
- SharedBroker::Validation.validate!(topic, message)
36
- encrypted_msg = SharedBroker::Cipher.encrypt(message, SharedBroker.encryption_key)
37
-
38
- @circuit_breaker.run do
39
- @adapter.publish(topic, encrypted_msg, correlation_id: correlation_id)
40
- end
41
- end
42
-
43
- def subscribe(topic, queue_name, max_retries: 3, backoff_base: 2, &block)
44
- @adapter.subscribe(topic, queue_name, max_retries: max_retries, backoff_base: backoff_base) do |raw_message|
45
- decrypted_msg = SharedBroker::Cipher.decrypt(raw_message, SharedBroker.encryption_key)
46
- SharedBroker::Validation.validate!(topic, decrypted_msg)
47
- block.call(decrypted_msg)
48
- end
49
- end
50
- end
51
- end
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "shared_broker/version"
4
+ require_relative "shared_broker/telemetry"
5
+ require_relative "shared_broker/circuit_breaker"
6
+ require_relative "shared_broker/validation"
7
+ require_relative "shared_broker/cipher"
8
+ require_relative "shared_broker/middleware_pipeline"
9
+ require_relative "shared_broker/middlewares/open_telemetry_propagation"
10
+ require_relative "shared_broker/adapters/base"
11
+ require_relative "shared_broker/adapters/in_memory"
12
+ require_relative "shared_broker/adapters/rabbit_mq"
13
+ require_relative "shared_broker/adapters/kafka"
14
+ require_relative "shared_broker/adapters/redis"
15
+
16
+ module SharedBroker
17
+ class << self
18
+ attr_accessor :encryption_key
19
+ end
20
+
21
+ # Default key for development/test if not set
22
+ @encryption_key = ENV.fetch("SHARED_BROKER_ENCRYPTION_KEY") { "a" * 32 }
23
+
24
+ class Client
25
+ attr_reader :circuit_breaker, :middleware_pipeline
26
+
27
+ def initialize(adapter:, circuit_breaker: nil, middlewares: nil)
28
+ unless adapter.respond_to?(:publish) && adapter.respond_to?(:subscribe)
29
+ raise ArgumentError, "Expected adapter to respond to :publish and :subscribe, got #{adapter.class} with value #{adapter.inspect}"
30
+ end
31
+
32
+ @adapter = adapter
33
+ @circuit_breaker = circuit_breaker || CircuitBreaker.new
34
+
35
+ resolved_middlewares = middlewares || [SharedBroker::Middlewares::OpenTelemetryPropagation.new]
36
+ @middleware_pipeline = MiddlewarePipeline.new(resolved_middlewares)
37
+ end
38
+
39
+ def publish(topic, message, correlation_id: nil)
40
+ metadata = { correlation_id: correlation_id, operation: :publish }
41
+ @middleware_pipeline.execute(topic, message, metadata) do
42
+ SharedBroker::Validation.validate!(topic, message)
43
+ encrypted_msg = SharedBroker::Cipher.encrypt(message, SharedBroker.encryption_key)
44
+
45
+ @circuit_breaker.run do
46
+ @adapter.publish(topic, encrypted_msg, correlation_id: correlation_id)
47
+ end
48
+ end
49
+ end
50
+
51
+ def subscribe(topic, queue_name, max_retries: 3, backoff_base: 2, &block)
52
+ @adapter.subscribe(topic, queue_name, max_retries: max_retries, backoff_base: backoff_base) do |raw_message|
53
+ decrypted_msg = SharedBroker::Cipher.decrypt(raw_message, SharedBroker.encryption_key)
54
+ SharedBroker::Validation.validate!(topic, decrypted_msg)
55
+
56
+ metadata = { correlation_id: decrypted_msg[:_correlation_id], operation: :subscribe, queue_name: queue_name }
57
+ @middleware_pipeline.execute(topic, decrypted_msg, metadata) do
58
+ block.call(decrypted_msg)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shared_broker
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
- - Gemini Antigravity
7
+ - Wesley Lima
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-06-06 00:00:00.000000000 Z
11
+ date: 2026-06-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bunny
@@ -125,7 +125,7 @@ dependencies:
125
125
  description: Shared library for asynchronous messaging, distributed tracing, and customizable
126
126
  adapters.
127
127
  email:
128
- - antigravity@google.com
128
+ - wesleyskap@gmail.com
129
129
  executables: []
130
130
  extensions: []
131
131
  extra_rdoc_files: []
@@ -141,6 +141,8 @@ files:
141
141
  - lib/shared_broker/adapters/redis.rb
142
142
  - lib/shared_broker/cipher.rb
143
143
  - lib/shared_broker/circuit_breaker.rb
144
+ - lib/shared_broker/middleware_pipeline.rb
145
+ - lib/shared_broker/middlewares/open_telemetry_propagation.rb
144
146
  - lib/shared_broker/telemetry.rb
145
147
  - lib/shared_broker/validation.rb
146
148
  - lib/shared_broker/version.rb
@@ -150,7 +152,7 @@ licenses:
150
152
  - MIT
151
153
  metadata:
152
154
  source_code_uri: https://github.com/wesleyskap/shared_broker
153
- changelog_uri: https://github.com/wesleyskap/shared_broker/blob/main/CHANGELOG.md
155
+ changelog_uri: https://github.com/wesleyskap/shared_broker/blob/master/CHANGELOG.md
154
156
  post_install_message:
155
157
  rdoc_options: []
156
158
  require_paths: