proto_turf 0.0.1 → 0.0.3
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/.rspec +2 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +106 -0
- data/README.md +8 -4
- data/lib/proto_turf/cached_confluent_schema_registry.rb +3 -5
- data/lib/proto_turf/confluent_schema_registry.rb +7 -9
- data/lib/proto_turf/version.rb +1 -1
- data/lib/proto_turf.rb +75 -30
- data/proto_turf.gemspec +14 -13
- data/spec/decoding_spec.rb +37 -0
- data/spec/encoding_spec.rb +58 -0
- data/spec/gen/referenced/referer_pb.rb +24 -0
- data/spec/gen/simple/simple_pb.rb +17 -0
- data/spec/schemas/referenced/referer.proto +24 -0
- data/spec/schemas/simple/simple.proto +7 -0
- data/spec/spec_helper.rb +13 -0
- metadata +55 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9af9eb4df4dd0323090363e65378d08d4b9a55050eb0a08ea6ddfe2cd7bd8b59
|
4
|
+
data.tar.gz: 32fb8b5f560d5e49978930f5e863d5851efc17b288f75c26e45ee7d1cf8e98dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3286859be78192d3495c3dc58e15dad50273aed4d611ddf579b9b27a16b01bbf0652d17af367192ed6dd9154a693a1991342439d53bde14777a5e61e5402a036
|
7
|
+
data.tar.gz: e8d941d3a7b153345e407ed62fb4e6201b93a65e01810f06cacb4fa616bbbf3c93b1456be349f3f53fd8e41d5a7f4fc4fcd0fad9fc380f8c7ff4ea101678fc18
|
data/.rspec
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## UNRELEASED
|
9
|
+
|
10
|
+
# 0.0.3 - 2025-8-15
|
11
|
+
|
12
|
+
* Initial release.
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
proto_turf (0.0.2)
|
5
|
+
excon
|
6
|
+
google-protobuf
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
addressable (2.8.7)
|
12
|
+
public_suffix (>= 2.0.2, < 7.0)
|
13
|
+
ast (2.4.3)
|
14
|
+
bigdecimal (3.2.2)
|
15
|
+
crack (1.0.0)
|
16
|
+
bigdecimal
|
17
|
+
rexml
|
18
|
+
diff-lcs (1.6.1)
|
19
|
+
excon (1.2.9)
|
20
|
+
logger
|
21
|
+
google-protobuf (4.30.2-arm64-darwin)
|
22
|
+
bigdecimal
|
23
|
+
rake (>= 13)
|
24
|
+
hashdiff (1.2.0)
|
25
|
+
json (2.13.2)
|
26
|
+
language_server-protocol (3.17.0.5)
|
27
|
+
lint_roller (1.1.0)
|
28
|
+
logger (1.7.0)
|
29
|
+
parallel (1.27.0)
|
30
|
+
parser (3.3.9.0)
|
31
|
+
ast (~> 2.4.1)
|
32
|
+
racc
|
33
|
+
prism (1.4.0)
|
34
|
+
public_suffix (6.0.2)
|
35
|
+
racc (1.8.1)
|
36
|
+
rainbow (3.1.1)
|
37
|
+
rake (13.3.0)
|
38
|
+
regexp_parser (2.11.2)
|
39
|
+
rexml (3.4.1)
|
40
|
+
rspec (3.13.0)
|
41
|
+
rspec-core (~> 3.13.0)
|
42
|
+
rspec-expectations (~> 3.13.0)
|
43
|
+
rspec-mocks (~> 3.13.0)
|
44
|
+
rspec-core (3.13.3)
|
45
|
+
rspec-support (~> 3.13.0)
|
46
|
+
rspec-expectations (3.13.4)
|
47
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
48
|
+
rspec-support (~> 3.13.0)
|
49
|
+
rspec-mocks (3.13.3)
|
50
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
51
|
+
rspec-support (~> 3.13.0)
|
52
|
+
rspec-support (3.13.3)
|
53
|
+
rubocop (1.75.8)
|
54
|
+
json (~> 2.3)
|
55
|
+
language_server-protocol (~> 3.17.0.2)
|
56
|
+
lint_roller (~> 1.1.0)
|
57
|
+
parallel (~> 1.10)
|
58
|
+
parser (>= 3.3.0.2)
|
59
|
+
rainbow (>= 2.2.2, < 4.0)
|
60
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
61
|
+
rubocop-ast (>= 1.44.0, < 2.0)
|
62
|
+
ruby-progressbar (~> 1.7)
|
63
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
64
|
+
rubocop-ast (1.46.0)
|
65
|
+
parser (>= 3.3.7.2)
|
66
|
+
prism (~> 1.4)
|
67
|
+
rubocop-performance (1.25.0)
|
68
|
+
lint_roller (~> 1.1)
|
69
|
+
rubocop (>= 1.75.0, < 2.0)
|
70
|
+
rubocop-ast (>= 1.38.0, < 2.0)
|
71
|
+
ruby-progressbar (1.13.0)
|
72
|
+
standard (1.50.0)
|
73
|
+
language_server-protocol (~> 3.17.0.2)
|
74
|
+
lint_roller (~> 1.0)
|
75
|
+
rubocop (~> 1.75.5)
|
76
|
+
standard-custom (~> 1.0.0)
|
77
|
+
standard-performance (~> 1.8)
|
78
|
+
standard-custom (1.0.2)
|
79
|
+
lint_roller (~> 1.0)
|
80
|
+
rubocop (~> 1.50)
|
81
|
+
standard-performance (1.8.0)
|
82
|
+
lint_roller (~> 1.1)
|
83
|
+
rubocop-performance (~> 1.25.0)
|
84
|
+
standardrb (1.0.1)
|
85
|
+
standard
|
86
|
+
unicode-display_width (3.1.5)
|
87
|
+
unicode-emoji (~> 4.0, >= 4.0.4)
|
88
|
+
unicode-emoji (4.0.4)
|
89
|
+
webmock (3.25.1)
|
90
|
+
addressable (>= 2.8.0)
|
91
|
+
crack (>= 0.3.2)
|
92
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
93
|
+
|
94
|
+
PLATFORMS
|
95
|
+
arm64-darwin
|
96
|
+
|
97
|
+
DEPENDENCIES
|
98
|
+
bundler (~> 2.0)
|
99
|
+
proto_turf!
|
100
|
+
rake (~> 13.0)
|
101
|
+
rspec (~> 3.2)
|
102
|
+
standardrb
|
103
|
+
webmock
|
104
|
+
|
105
|
+
BUNDLED WITH
|
106
|
+
2.6.7
|
data/README.md
CHANGED
@@ -27,7 +27,7 @@ Example usage:
|
|
27
27
|
```ruby
|
28
28
|
require 'proto_turf'
|
29
29
|
|
30
|
-
proto_turf = ProtoTurf.new(registry_url: 'http://localhost:8081')
|
30
|
+
proto_turf = ProtoTurf.new(registry_url: 'http://localhost:8081', schema_paths: ['path/to/protos'])
|
31
31
|
message = MyProto::MyMessage.new(field1: 'value1', field2: 42)
|
32
32
|
encoded = proto_turf.encode(message, subject: 'my-subject')
|
33
33
|
|
@@ -38,11 +38,15 @@ decoded_proto_message = proto_turf.decode(encoded_string)
|
|
38
38
|
|
39
39
|
If you're using [buf](https://buf.build/) to manage your Protobuf definitions, you should run `buf export` before using `proto_turf` to ensure that all the dependencies are available as `.proto` files in your project. The actual proto text is needed when registering the schema with the Schema Registry.
|
40
40
|
|
41
|
+
Because `buf export` overwrites/deletes existing files, you should run it into a different directory and provide both as `schema_paths` to the `ProtoTurf` constructor.
|
42
|
+
|
41
43
|
## Notes about usage
|
42
44
|
|
43
|
-
* For now, this library only supports a single message per `.proto` file that is registered with the Schema Registry. You can reference as many other files and messages as you like from that message, but keeping it to one message per file simplifies the workflow significantly.
|
44
45
|
* When decoding, this library does *not* attempt to fully parse the Proto definition stored on the schema registry and generate dynamic classes. Instead, it simply parses out the package and message and assumes that the reader has the message available in the descriptor pool. Any compatibility issues should be detected through normal means, i.e. just by instantiating the message and seeing if any errors are raised.
|
45
46
|
|
46
|
-
|
47
|
+
### Regenerating test protos
|
48
|
+
Run the following to regenerate:
|
47
49
|
|
48
|
-
|
50
|
+
```sh
|
51
|
+
protoc -I spec/schemas --ruby_out=spec/gen --ruby_opt=paths=source_relative spec/schemas/**/*.proto
|
52
|
+
```
|
@@ -1,5 +1,4 @@
|
|
1
1
|
class ProtoTurf::CachedConfluentSchemaRegistry
|
2
|
-
|
3
2
|
# @param upstream [ProtoTurf::ConfluentSchemaRegistry]
|
4
3
|
def initialize(upstream)
|
5
4
|
@upstream = upstream
|
@@ -9,7 +8,7 @@ class ProtoTurf::CachedConfluentSchemaRegistry
|
|
9
8
|
end
|
10
9
|
|
11
10
|
# Delegate the following methods to the upstream
|
12
|
-
%i
|
11
|
+
%i[subject_versions schema_subject_versions].each do |name|
|
13
12
|
define_method(name) do |*args|
|
14
13
|
instance_variable_get(:@upstream).send(name, *args)
|
15
14
|
end
|
@@ -29,14 +28,14 @@ class ProtoTurf::CachedConfluentSchemaRegistry
|
|
29
28
|
return @versions_by_subject_and_id[key] if @versions_by_subject_and_id[key]
|
30
29
|
|
31
30
|
results = @upstream.schema_subject_versions(id)
|
32
|
-
@versions_by_subject_and_id[key] = results&.find { |r| r[
|
31
|
+
@versions_by_subject_and_id[key] = results&.find { |r| r["subject"] == subject }&.dig("version")
|
33
32
|
end
|
34
33
|
|
35
34
|
# @param subject [String] the subject to check
|
36
35
|
# @param schema [String] the schema text to check
|
37
36
|
# @return [Boolean] true if we know the schema has been registered for that subject.
|
38
37
|
def registered?(subject, schema)
|
39
|
-
@ids_by_schema[[subject, schema]].
|
38
|
+
@ids_by_schema[[subject, schema]] && !@ids_by_schema[[subject, schema]].empty?
|
40
39
|
end
|
41
40
|
|
42
41
|
# @param subject [String] the subject to register the schema under
|
@@ -47,5 +46,4 @@ class ProtoTurf::CachedConfluentSchemaRegistry
|
|
47
46
|
|
48
47
|
@ids_by_schema[key] ||= @upstream.register(subject, schema, references: references)
|
49
48
|
end
|
50
|
-
|
51
49
|
end
|
@@ -1,8 +1,7 @@
|
|
1
|
-
require
|
1
|
+
require "excon"
|
2
2
|
|
3
3
|
class ProtoTurf
|
4
4
|
class ConfluentSchemaRegistry
|
5
|
-
|
6
5
|
CONTENT_TYPE = "application/vnd.schemaregistry.v1+json".freeze
|
7
6
|
|
8
7
|
def initialize(
|
@@ -24,7 +23,7 @@ class ProtoTurf
|
|
24
23
|
retry_limit: nil
|
25
24
|
)
|
26
25
|
@path_prefix = path_prefix
|
27
|
-
@schema_context_prefix = schema_context.nil? ?
|
26
|
+
@schema_context_prefix = schema_context.nil? ? "" : ":.#{schema_context}:"
|
28
27
|
@schema_context_options = schema_context.nil? ? {} : {query: {subject: @schema_context_prefix}}
|
29
28
|
@logger = logger
|
30
29
|
headers = Excon.defaults[:headers].merge(
|
@@ -57,7 +56,7 @@ class ProtoTurf
|
|
57
56
|
# @return [String] the schema string stored in the registry for the given id
|
58
57
|
def fetch(id)
|
59
58
|
@logger.info "Fetching schema with id #{id}"
|
60
|
-
data = get("/schemas/ids/#{id}", idempotent: true, **@schema_context_options
|
59
|
+
data = get("/schemas/ids/#{id}", idempotent: true, **@schema_context_options)
|
61
60
|
data.fetch("schema")
|
62
61
|
end
|
63
62
|
|
@@ -73,9 +72,9 @@ class ProtoTurf
|
|
73
72
|
# @return [Integer] the ID of the registered schema
|
74
73
|
def register(subject, schema, references: [])
|
75
74
|
data = post("/subjects/#{@schema_context_prefix}#{CGI.escapeURIComponent(subject)}/versions",
|
76
|
-
|
77
|
-
|
78
|
-
|
75
|
+
body: {schemaType: "PROTOBUF",
|
76
|
+
references: references,
|
77
|
+
schema: schema.to_s}.to_json)
|
79
78
|
|
80
79
|
id = data.fetch("id")
|
81
80
|
|
@@ -105,7 +104,7 @@ class ProtoTurf
|
|
105
104
|
end
|
106
105
|
|
107
106
|
def request(path, **options)
|
108
|
-
options = {
|
107
|
+
options = {expects: 200}.merge!(options)
|
109
108
|
path = File.join(@path_prefix, path) unless @path_prefix.nil?
|
110
109
|
response = @connection.request(path: path, **options)
|
111
110
|
JSON.parse(response.body)
|
@@ -113,6 +112,5 @@ class ProtoTurf
|
|
113
112
|
@logger.error("Error while requesting #{path}: #{e.response.body}")
|
114
113
|
raise
|
115
114
|
end
|
116
|
-
|
117
115
|
end
|
118
116
|
end
|
data/lib/proto_turf/version.rb
CHANGED
data/lib/proto_turf.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require "logger"
|
2
|
+
require "google/protobuf"
|
3
|
+
require "google/protobuf/well_known_types"
|
4
|
+
require "google/protobuf/descriptor_pb"
|
5
|
+
require "json"
|
6
|
+
require "proto_turf/confluent_schema_registry"
|
7
|
+
require "proto_turf/cached_confluent_schema_registry"
|
4
8
|
|
5
9
|
class ProtoTurf
|
6
10
|
# Provides a way to encode and decode messages without having to embed schemas
|
@@ -15,14 +19,13 @@ class ProtoTurf
|
|
15
19
|
# https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format
|
16
20
|
MAGIC_BYTE = [0].pack("C").freeze
|
17
21
|
|
18
|
-
|
19
22
|
# Instantiate a new ProtoTurf instance with the given configuration.
|
20
23
|
#
|
21
24
|
# registry - A schema registry object that responds to all methods in the
|
22
25
|
# ProtoTurf::ConfluentSchemaRegistry interface.
|
23
26
|
# registry_url - The String URL of the schema registry that should be used.
|
24
27
|
# schema_context - Schema registry context name (optional)
|
25
|
-
#
|
28
|
+
# schema_paths - The String file system path where local schemas are stored.
|
26
29
|
# registry_path_prefix - The String URL path prefix used to namespace schema registry requests (optional).
|
27
30
|
# logger - The Logger that should be used to log information (optional).
|
28
31
|
# proxy - Forward the request via proxy (optional).
|
@@ -40,7 +43,7 @@ class ProtoTurf
|
|
40
43
|
registry: nil,
|
41
44
|
registry_url: nil,
|
42
45
|
schema_context: nil,
|
43
|
-
|
46
|
+
schema_paths: nil,
|
44
47
|
registry_path_prefix: nil,
|
45
48
|
logger: nil,
|
46
49
|
proxy: nil,
|
@@ -57,7 +60,7 @@ class ProtoTurf
|
|
57
60
|
retry_limit: nil
|
58
61
|
)
|
59
62
|
@logger = logger || Logger.new($stderr)
|
60
|
-
@
|
63
|
+
@paths = Array(schema_paths)
|
61
64
|
@registry = registry || ProtoTurf::CachedConfluentSchemaRegistry.new(
|
62
65
|
ProtoTurf::ConfluentSchemaRegistry.new(
|
63
66
|
registry_url,
|
@@ -90,7 +93,8 @@ class ProtoTurf
|
|
90
93
|
def encode(message, subject: nil)
|
91
94
|
load_schemas! if @all_schemas.empty?
|
92
95
|
|
93
|
-
|
96
|
+
file_descriptor = message.class.descriptor.file_descriptor
|
97
|
+
id = register_schema(file_descriptor, subject: subject)
|
94
98
|
|
95
99
|
stream = StringIO.new
|
96
100
|
# Always start with the magic byte.
|
@@ -99,9 +103,15 @@ class ProtoTurf
|
|
99
103
|
# The schema id is encoded as a 4-byte big-endian integer.
|
100
104
|
stream.write([id].pack("N"))
|
101
105
|
|
102
|
-
|
103
|
-
|
104
|
-
|
106
|
+
_, indexes = find_index(message.class.descriptor.to_proto,
|
107
|
+
file_descriptor.to_proto.message_type)
|
108
|
+
|
109
|
+
if indexes == [0]
|
110
|
+
write_int(stream, 0)
|
111
|
+
else
|
112
|
+
write_int(stream, indexes.length)
|
113
|
+
indexes.each { |i| write_int(stream, i) }
|
114
|
+
end
|
105
115
|
|
106
116
|
# Now we write the actual message.
|
107
117
|
stream.write(message.to_proto)
|
@@ -131,21 +141,53 @@ class ProtoTurf
|
|
131
141
|
end
|
132
142
|
|
133
143
|
# The schema id is a 4-byte big-endian integer.
|
134
|
-
schema_id = stream.read(4).
|
144
|
+
schema_id = stream.read(4).unpack1("N")
|
135
145
|
|
136
146
|
# For now, we're only going to support a single message per schema. See
|
137
147
|
# https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format
|
138
|
-
read_int(stream)
|
148
|
+
index_length = read_int(stream)
|
149
|
+
indexes = []
|
150
|
+
if index_length.zero?
|
151
|
+
indexes.push(0)
|
152
|
+
else
|
153
|
+
index_length.times do
|
154
|
+
indexes.push(read_int(stream))
|
155
|
+
end
|
156
|
+
end
|
139
157
|
|
140
158
|
schema = @registry.fetch(schema_id)
|
141
159
|
encoded = stream.read
|
142
|
-
decode_protobuf(schema, encoded)
|
160
|
+
decode_protobuf(schema, encoded, indexes)
|
143
161
|
rescue Excon::Error::NotFound
|
144
162
|
raise SchemaNotFoundError.new("Schema with id: #{schema_id} is not found on registry")
|
145
163
|
end
|
146
164
|
|
147
165
|
private
|
148
166
|
|
167
|
+
def find_index(descriptor, messages, indexes = [])
|
168
|
+
messages.each_with_index do |sub_descriptor, i|
|
169
|
+
if sub_descriptor == descriptor
|
170
|
+
indexes.push(i)
|
171
|
+
return [true, indexes]
|
172
|
+
else
|
173
|
+
found, found_indexes = find_index(descriptor, sub_descriptor.nested_type, indexes + [i])
|
174
|
+
return [true, found_indexes] if found
|
175
|
+
end
|
176
|
+
end
|
177
|
+
[]
|
178
|
+
end
|
179
|
+
|
180
|
+
def find_descriptor(indexes, messages)
|
181
|
+
first_index = indexes.shift
|
182
|
+
message = messages[first_index]
|
183
|
+
path = [message.name]
|
184
|
+
while indexes.length.positive?
|
185
|
+
message = message.nested_type[indexes.shift]
|
186
|
+
path.push(message.name)
|
187
|
+
end
|
188
|
+
path
|
189
|
+
end
|
190
|
+
|
149
191
|
# Write an int with zig-zag encoding. Copied from Avro.
|
150
192
|
def write_int(stream, n)
|
151
193
|
n = (n << 1) ^ (n >> 63)
|
@@ -169,7 +211,7 @@ class ProtoTurf
|
|
169
211
|
(n >> 1) ^ -(n & 1)
|
170
212
|
end
|
171
213
|
|
172
|
-
def decode_protobuf(schema, encoded)
|
214
|
+
def decode_protobuf(schema, encoded, indexes)
|
173
215
|
# get the package
|
174
216
|
package = schema.match(/package (\S+);/)[1]
|
175
217
|
# get the first message in the protobuf text
|
@@ -181,7 +223,9 @@ class ProtoTurf
|
|
181
223
|
unless descriptor
|
182
224
|
raise "Could not find schema for #{full_name}. Make sure the corresponding .proto file has been compiled and loaded."
|
183
225
|
end
|
184
|
-
descriptor.
|
226
|
+
path = find_descriptor(indexes, descriptor.file_descriptor.to_proto.message_type)
|
227
|
+
correct_message = Google::Protobuf::DescriptorPool.generated_pool.lookup("#{package}.#{path.join(".")}")
|
228
|
+
correct_message.msgclass.decode(encoded)
|
185
229
|
end
|
186
230
|
|
187
231
|
def register_schema(file_descriptor, subject: nil)
|
@@ -189,7 +233,7 @@ class ProtoTurf
|
|
189
233
|
return if @registry.registered?(file_descriptor.name, subject)
|
190
234
|
|
191
235
|
# register dependencies first
|
192
|
-
dependencies = file_descriptor.to_proto.dependency.to_a.reject { |d| d.start_with?(
|
236
|
+
dependencies = file_descriptor.to_proto.dependency.to_a.reject { |d| d.start_with?("google/protobuf/") }
|
193
237
|
versions = dependencies.map do |dependency|
|
194
238
|
dependency_descriptor = @all_schemas[dependency]
|
195
239
|
result = register_schema(dependency_descriptor, subject: dependency_descriptor.name)
|
@@ -197,20 +241,22 @@ class ProtoTurf
|
|
197
241
|
end
|
198
242
|
|
199
243
|
@registry.register(subject,
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
)
|
244
|
+
schema_text(file_descriptor),
|
245
|
+
references: dependencies.map.with_index do |dependency, i|
|
246
|
+
{
|
247
|
+
name: dependency,
|
248
|
+
subject: dependency,
|
249
|
+
version: versions[i]
|
250
|
+
}
|
251
|
+
end)
|
209
252
|
end
|
210
253
|
|
211
254
|
def schema_text(file_descriptor)
|
212
|
-
|
213
|
-
|
255
|
+
@paths.each do |path|
|
256
|
+
filename = "#{path}/#{file_descriptor.name}"
|
257
|
+
return File.read(filename) if File.exist?(filename)
|
258
|
+
end
|
259
|
+
""
|
214
260
|
end
|
215
261
|
|
216
262
|
def load_schemas!
|
@@ -220,10 +266,9 @@ class ProtoTurf
|
|
220
266
|
all_messages.each do |m|
|
221
267
|
file_desc = m.descriptor.file_descriptor
|
222
268
|
file_path = file_desc.name
|
223
|
-
next if file_path.start_with?(
|
269
|
+
next if file_path.start_with?("google/protobuf/") # skip built-in protos
|
224
270
|
|
225
271
|
@all_schemas[file_path] = file_desc
|
226
272
|
end
|
227
273
|
end
|
228
|
-
|
229
274
|
end
|
data/proto_turf.gemspec
CHANGED
@@ -1,27 +1,28 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
3
|
+
require "proto_turf/version"
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name
|
8
|
-
spec.version
|
9
|
-
spec.authors
|
10
|
-
spec.email
|
11
|
-
spec.summary
|
12
|
-
spec.homepage
|
13
|
-
spec.license
|
6
|
+
spec.name = "proto_turf"
|
7
|
+
spec.version = ProtoTurf::VERSION
|
8
|
+
spec.authors = ["Daniel Orner"]
|
9
|
+
spec.email = ["daniel.orner@flipp.com"]
|
10
|
+
spec.summary = "Support for Protobuf files in Confluent Schema Registry"
|
11
|
+
spec.homepage = "https://github.com/flipp-oss/proto_turf"
|
12
|
+
spec.license = "MIT"
|
14
13
|
|
15
14
|
spec.metadata["rubygems_mfa_required"] = "true"
|
16
15
|
|
17
|
-
spec.files
|
18
|
-
spec.executables
|
19
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
18
|
spec.require_paths = ["lib"]
|
21
19
|
|
22
20
|
spec.add_dependency "google-protobuf"
|
21
|
+
spec.add_dependency "excon"
|
23
22
|
|
24
23
|
spec.add_development_dependency "bundler", "~> 2.0"
|
25
24
|
spec.add_development_dependency "rake", "~> 13.0"
|
26
25
|
spec.add_development_dependency "rspec", "~> 3.2"
|
26
|
+
spec.add_development_dependency "webmock"
|
27
|
+
spec.add_development_dependency "standardrb"
|
27
28
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
RSpec.describe "encoding" do
|
2
|
+
let(:proto_turf) do
|
3
|
+
ProtoTurf.new(
|
4
|
+
registry_url: "http://localhost:8081",
|
5
|
+
schema_paths: ["spec/schemas"]
|
6
|
+
)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should decode a simple message" do
|
10
|
+
schema = File.read("#{__dir__}/schemas/simple/simple.proto")
|
11
|
+
stub = stub_request(:get, "http://localhost:8081/schemas/ids/15")
|
12
|
+
.to_return_json(body: {schema: schema})
|
13
|
+
msg = Simple::V1::SimpleMessage.new(name: "my name")
|
14
|
+
encoded = "\u0000\u0000\u0000\u0000\u000F\u0000" + msg.to_proto
|
15
|
+
expect(proto_turf.decode(encoded)).to eq(msg)
|
16
|
+
|
17
|
+
# if we do it again we should not see any more requests
|
18
|
+
expect(proto_turf.decode(encoded)).to eq(msg)
|
19
|
+
|
20
|
+
expect(stub).to have_been_requested.once
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should decode a complex message" do
|
24
|
+
schema = File.read("#{__dir__}/schemas/referenced/referer.proto")
|
25
|
+
stub = stub_request(:get, "http://localhost:8081/schemas/ids/20")
|
26
|
+
.to_return_json(body: {schema: schema})
|
27
|
+
msg = Referenced::V1::MessageB::MessageBA.new(
|
28
|
+
simple: Simple::V1::SimpleMessage.new(name: "my name")
|
29
|
+
)
|
30
|
+
encoded = "\u0000\u0000\u0000\u0000\u0014\u0004\u0002\u0000" + msg.to_proto
|
31
|
+
expect(proto_turf.decode(encoded)).to eq(msg)
|
32
|
+
|
33
|
+
# if we do it again we should not see any more requests
|
34
|
+
expect(proto_turf.decode(encoded)).to eq(msg)
|
35
|
+
expect(stub).to have_been_requested.once
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
RSpec.describe "encoding" do
|
2
|
+
let(:proto_turf) do
|
3
|
+
ProtoTurf.new(
|
4
|
+
registry_url: "http://localhost:8081",
|
5
|
+
schema_paths: ["spec/schemas"]
|
6
|
+
)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should encode a simple message" do
|
10
|
+
schema = File.read("#{__dir__}/schemas/simple/simple.proto")
|
11
|
+
stub = stub_request(:post, "http://localhost:8081/subjects/simple/versions")
|
12
|
+
.with(body: {"schemaType" => "PROTOBUF",
|
13
|
+
"references" => [],
|
14
|
+
"schema" => schema}).to_return_json(body: {id: 15})
|
15
|
+
msg = Simple::V1::SimpleMessage.new(name: "my name")
|
16
|
+
encoded = proto_turf.encode(msg, subject: "simple")
|
17
|
+
expect(encoded).to eq("\u0000\u0000\u0000\u0000\u000F\u0000" + msg.to_proto)
|
18
|
+
|
19
|
+
# if we do it again we should not see any more requests
|
20
|
+
encoded2 = proto_turf.encode(msg, subject: "simple")
|
21
|
+
expect(encoded2).to eq(encoded)
|
22
|
+
|
23
|
+
expect(stub).to have_been_requested.once
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should encode a complex message" do
|
27
|
+
schema = File.read("#{__dir__}/schemas/referenced/referer.proto")
|
28
|
+
dep_schema = File.read("#{__dir__}/schemas/simple/simple.proto")
|
29
|
+
dep_stub = stub_request(:post, "http://localhost:8081/subjects/simple%2Fsimple.proto/versions")
|
30
|
+
.with(body: {"schemaType" => "PROTOBUF",
|
31
|
+
"references" => [],
|
32
|
+
"schema" => dep_schema}).to_return_json(body: {id: 15})
|
33
|
+
version_stub = stub_request(:get, "http://localhost:8081/schemas/ids/15/versions")
|
34
|
+
.to_return_json(body: [{version: 1, subject: "simple/simple.proto"}])
|
35
|
+
stub = stub_request(:post, "http://localhost:8081/subjects/referenced/versions")
|
36
|
+
.with(body: {"schemaType" => "PROTOBUF",
|
37
|
+
"references" => [
|
38
|
+
{
|
39
|
+
name: "simple/simple.proto",
|
40
|
+
subject: "simple/simple.proto",
|
41
|
+
version: 1
|
42
|
+
}
|
43
|
+
],
|
44
|
+
"schema" => schema}).to_return_json(body: {id: 20})
|
45
|
+
msg = Referenced::V1::MessageB::MessageBA.new(
|
46
|
+
simple: Simple::V1::SimpleMessage.new(name: "my name")
|
47
|
+
)
|
48
|
+
encoded = proto_turf.encode(msg, subject: "referenced")
|
49
|
+
expect(encoded).to eq("\u0000\u0000\u0000\u0000\u0014\u0004\u0002\u0000" + msg.to_proto)
|
50
|
+
|
51
|
+
# if we do it again we should not see any more requests
|
52
|
+
encoded2 = proto_turf.encode(msg, subject: "referenced")
|
53
|
+
expect(encoded2).to eq(encoded)
|
54
|
+
expect(stub).to have_been_requested.once
|
55
|
+
expect(dep_stub).to have_been_requested.once
|
56
|
+
expect(version_stub).to have_been_requested.once
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
4
|
+
# source: referenced/referer.proto
|
5
|
+
|
6
|
+
require "google/protobuf"
|
7
|
+
|
8
|
+
require "simple/simple_pb"
|
9
|
+
|
10
|
+
descriptor_data = "\n\x18referenced/referer.proto\x12\rreferenced.v1\x1a\x13simple/simple.proto\"j\n\x08MessageA\x1a\x43\n\tMessageAA\x12\x0c\n\x04name\x18\x01 \x01(\t\x12(\n\x06simple\x18\x02 \x01(\x0b\x32\x18.simple.v1.SimpleMessage\x1a\x19\n\tMessageAB\x12\x0c\n\x04name\x18\x01 \x01(\t\"j\n\x08MessageB\x1a\x43\n\tMessageBA\x12\x0c\n\x04name\x18\x01 \x01(\t\x12(\n\x06simple\x18\x02 \x01(\x0b\x32\x18.simple.v1.SimpleMessage\x1a\x19\n\tMessageBB\x12\x0c\n\x04name\x18\x01 \x01(\tb\x06proto3"
|
11
|
+
|
12
|
+
pool = Google::Protobuf::DescriptorPool.generated_pool
|
13
|
+
pool.add_serialized_file(descriptor_data)
|
14
|
+
|
15
|
+
module Referenced
|
16
|
+
module V1
|
17
|
+
MessageA = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("referenced.v1.MessageA").msgclass
|
18
|
+
MessageA::MessageAA = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("referenced.v1.MessageA.MessageAA").msgclass
|
19
|
+
MessageA::MessageAB = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("referenced.v1.MessageA.MessageAB").msgclass
|
20
|
+
MessageB = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("referenced.v1.MessageB").msgclass
|
21
|
+
MessageB::MessageBA = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("referenced.v1.MessageB.MessageBA").msgclass
|
22
|
+
MessageB::MessageBB = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("referenced.v1.MessageB.MessageBB").msgclass
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
4
|
+
# source: simple/simple.proto
|
5
|
+
|
6
|
+
require "google/protobuf"
|
7
|
+
|
8
|
+
descriptor_data = "\n\x13simple/simple.proto\x12\tsimple.v1\"\x1d\n\rSimpleMessage\x12\x0c\n\x04name\x18\x01 \x01(\tb\x06proto3"
|
9
|
+
|
10
|
+
pool = Google::Protobuf::DescriptorPool.generated_pool
|
11
|
+
pool.add_serialized_file(descriptor_data)
|
12
|
+
|
13
|
+
module Simple
|
14
|
+
module V1
|
15
|
+
SimpleMessage = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("simple.v1.SimpleMessage").msgclass
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
syntax = "proto3";
|
2
|
+
|
3
|
+
package referenced.v1;
|
4
|
+
import "simple/simple.proto";
|
5
|
+
|
6
|
+
message MessageA {
|
7
|
+
message MessageAA {
|
8
|
+
string name = 1;
|
9
|
+
simple.v1.SimpleMessage simple = 2;
|
10
|
+
}
|
11
|
+
message MessageAB {
|
12
|
+
string name = 1;
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
message MessageB {
|
17
|
+
message MessageBA {
|
18
|
+
string name = 1;
|
19
|
+
simple.v1.SimpleMessage simple = 2;
|
20
|
+
}
|
21
|
+
message MessageBB {
|
22
|
+
string name = 1;
|
23
|
+
}
|
24
|
+
}
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
4
|
+
$LOAD_PATH.unshift(File.expand_path("gen", __dir__))
|
5
|
+
Dir["#{__dir__}/gen/**/*.rb"].each { |file| require file }
|
6
|
+
require "proto_turf"
|
7
|
+
require "webmock/rspec"
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.full_backtrace = true
|
11
|
+
|
12
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
13
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: proto_turf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Orner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-08-
|
11
|
+
date: 2025-08-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: google-protobuf
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: excon
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +80,34 @@ dependencies:
|
|
66
80
|
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '3.2'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: webmock
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: standardrb
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
69
111
|
description:
|
70
112
|
email:
|
71
113
|
- daniel.orner@flipp.com
|
@@ -73,6 +115,10 @@ executables: []
|
|
73
115
|
extensions: []
|
74
116
|
extra_rdoc_files: []
|
75
117
|
files:
|
118
|
+
- ".rspec"
|
119
|
+
- CHANGELOG.md
|
120
|
+
- Gemfile
|
121
|
+
- Gemfile.lock
|
76
122
|
- LICENSE
|
77
123
|
- README.md
|
78
124
|
- lib/proto_turf.rb
|
@@ -80,6 +126,13 @@ files:
|
|
80
126
|
- lib/proto_turf/confluent_schema_registry.rb
|
81
127
|
- lib/proto_turf/version.rb
|
82
128
|
- proto_turf.gemspec
|
129
|
+
- spec/decoding_spec.rb
|
130
|
+
- spec/encoding_spec.rb
|
131
|
+
- spec/gen/referenced/referer_pb.rb
|
132
|
+
- spec/gen/simple/simple_pb.rb
|
133
|
+
- spec/schemas/referenced/referer.proto
|
134
|
+
- spec/schemas/simple/simple.proto
|
135
|
+
- spec/spec_helper.rb
|
83
136
|
homepage: https://github.com/flipp-oss/proto_turf
|
84
137
|
licenses:
|
85
138
|
- MIT
|