Skip to content

Commit 108dceb

Browse files
authored
[Feature] Implement Payload Converters (#49)
* Rename dummy Converter into DataConverter * Implement converter interface and all the default converters * Rename Converter to PayloadConverter * Add default payload converters * Add tests for the DataConverter
1 parent 17a0b72 commit 108dceb

27 files changed

+552
-26
lines changed

lib/temporal/client.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
require 'securerandom'
33
require 'socket'
44
require 'temporal/errors'
5-
require 'temporal/converter'
5+
require 'temporal/data_converter'
66
require 'temporal/client/workflow_handle'
77
require 'temporal/client/implementation'
88
require 'temporal/workflow/id_reuse_policy'
@@ -14,8 +14,8 @@ class Client
1414
# TODO: More argument to follow for converters, codecs, etc
1515
def initialize(connection, namespace, interceptors: [])
1616
@namespace = namespace
17-
converter = Converter.new
18-
@implementation = Client::Implementation.new(connection, namespace, converter, interceptors)
17+
data_converter = DataConverter.new
18+
@implementation = Client::Implementation.new(connection, namespace, data_converter, interceptors)
1919
end
2020

2121
def start_workflow( # rubocop:disable Metrics/ParameterLists

lib/temporal/converter.rb renamed to lib/temporal/data_converter.rb

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
require 'json'
1+
require 'temporal/payload_converter'
22

3-
# TODO: This is a dummy converter, a proper implementation will follow
43
module Temporal
5-
class Converter
6-
JSON_ENCODING = 'json/plain'.freeze
4+
class DataConverter
5+
def initialize(payload_converter: Temporal::PayloadConverter::DEFAULT)
6+
@payload_converter = payload_converter
7+
end
78

89
def to_payloads(data)
910
return if data.nil? || Array(data).empty?
@@ -33,17 +34,14 @@ def from_payload_map(payload_map)
3334

3435
private
3536

37+
attr_reader :payload_converter
38+
3639
def to_payload(data)
37-
Temporal::Api::Common::V1::Payload.new(
38-
metadata: { 'encoding' => JSON_ENCODING },
39-
data: JSON.generate(data).b,
40-
)
40+
payload_converter.to_payload(data)
4141
end
4242

4343
def from_payload(payload)
44-
if payload.metadata['encoding'] == JSON_ENCODING
45-
JSON.parse(payload.data)
46-
end
44+
payload_converter.from_payload(payload)
4745
end
4846
end
4947
end

lib/temporal/payload_converter.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
require 'temporal/payload_converter/bytes'
2+
require 'temporal/payload_converter/composite'
3+
require 'temporal/payload_converter/json'
4+
require 'temporal/payload_converter/nil'
5+
6+
module Temporal
7+
module PayloadConverter
8+
DEFAULT = Temporal::PayloadConverter::Composite.new(
9+
Temporal::PayloadConverter::Nil.new,
10+
Temporal::PayloadConverter::Bytes.new,
11+
Temporal::PayloadConverter::JSON.new,
12+
)
13+
end
14+
end
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module Temporal
2+
module PayloadConverter
3+
class Base
4+
def to_payload(_data)
5+
raise NoMethodError, 'must implement #to_payload'
6+
end
7+
8+
def from_payload(_payload)
9+
raise NoMethodError, 'must implement #from_payload'
10+
end
11+
end
12+
end
13+
end
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
require 'temporal/payload_converter/encoding_base'
2+
3+
module Temporal
4+
module PayloadConverter
5+
class Bytes < EncodingBase
6+
ENCODING = 'binary/plain'.freeze
7+
8+
def encoding
9+
ENCODING
10+
end
11+
12+
def from_payload(payload)
13+
payload.data
14+
end
15+
16+
def to_payload(data)
17+
return nil unless data.is_a?(String) && data.encoding == Encoding::ASCII_8BIT
18+
19+
Temporal::Api::Common::V1::Payload.new(
20+
metadata: { 'encoding' => ENCODING },
21+
data: data,
22+
)
23+
end
24+
end
25+
end
26+
end
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
require 'temporal/payload_converter/base'
2+
require 'temporal/errors'
3+
4+
module Temporal
5+
module PayloadConverter
6+
class Composite < Base
7+
class ConverterNotFound < Temporal::Error; end
8+
class EncodingNotSet < Temporal::Error; end
9+
10+
def initialize(*converters)
11+
super()
12+
13+
@converters = converters.each_with_object({}) do |converter, result|
14+
result[converter.encoding] = converter
15+
result
16+
end
17+
end
18+
19+
def to_payload(data)
20+
converters.each_value do |converter|
21+
payload = converter.to_payload(data)
22+
return payload unless payload.nil?
23+
end
24+
25+
available = converters.values.map(&:class).join(', ')
26+
raise ConverterNotFound, "Available converters (#{available}) could not convert data"
27+
end
28+
29+
def from_payload(payload)
30+
encoding = payload.metadata['encoding']
31+
raise EncodingNotSet, 'Missing payload encoding' unless encoding
32+
33+
converter = converters[encoding]
34+
unless converter
35+
available = converters.keys.join(', ')
36+
raise ConverterNotFound, "Missing converter for encoding '#{encoding}' (available: #{available})"
37+
end
38+
39+
converter.from_payload(payload)
40+
end
41+
42+
private
43+
44+
attr_reader :converters
45+
end
46+
end
47+
end
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
require 'temporal/api/common/v1/message_pb'
2+
3+
module Temporal
4+
module PayloadConverter
5+
class EncodingBase
6+
def encoding
7+
raise NoMethodError, 'must implement #encoding'
8+
end
9+
10+
def to_payload(_data)
11+
raise NoMethodError, 'must implement #to_payload'
12+
end
13+
14+
def from_payload(_payload)
15+
raise NoMethodError, 'must implement #from_payload'
16+
end
17+
end
18+
end
19+
end
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
require 'json/ext'
2+
require 'temporal/payload_converter/encoding_base'
3+
4+
module Temporal
5+
module PayloadConverter
6+
class JSON < EncodingBase
7+
ENCODING = 'json/plain'.freeze
8+
9+
def encoding
10+
ENCODING
11+
end
12+
13+
def from_payload(payload)
14+
::JSON.parse(payload.data, create_additions: true)
15+
end
16+
17+
def to_payload(data)
18+
Temporal::Api::Common::V1::Payload.new(
19+
metadata: { 'encoding' => ENCODING },
20+
data: ::JSON.generate(data).b,
21+
)
22+
end
23+
end
24+
end
25+
end

lib/temporal/payload_converter/nil.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
require 'temporal/payload_converter/encoding_base'
2+
3+
module Temporal
4+
module PayloadConverter
5+
class Nil < EncodingBase
6+
ENCODING = 'binary/null'.freeze
7+
8+
def encoding
9+
ENCODING
10+
end
11+
12+
def from_payload(_payload)
13+
nil
14+
end
15+
16+
def to_payload(data)
17+
return nil unless data.nil?
18+
19+
Temporal::Api::Common::V1::Payload.new(
20+
metadata: { 'encoding' => ENCODING },
21+
)
22+
end
23+
end
24+
end
25+
end

sig/payload_converter.rbs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module Temporal
2+
module PayloadConverter
3+
DEFAULT: _PayloadConverter
4+
end
5+
end

0 commit comments

Comments
 (0)