proto_turf 0.0.2 → 0.0.4
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/.github/workflows/lint.yml +18 -0
- data/.github/workflows/release.yml +31 -0
- data/.github/workflows/test.yml +22 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +5 -1
- data/Gemfile +5 -0
- data/Gemfile.lock +110 -0
- data/README.md +9 -1
- data/Rakefile +3 -0
- 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/proto_text.rb +315 -0
- data/lib/proto_turf/version.rb +1 -1
- data/lib/proto_turf.rb +25 -30
- data/proto_turf.gemspec +14 -13
- data/spec/decoding_spec.rb +36 -0
- data/spec/encoding_spec.rb +57 -0
- data/spec/gen/everything/everything_pb.rb +26 -0
- data/spec/gen/referenced/referer_pb.rb +24 -0
- data/spec/gen/simple/simple_pb.rb +18 -0
- data/spec/proto_text_spec.rb +8 -0
- data/spec/schemas/everything/everything.proto +105 -0
- data/spec/schemas/referenced/referer.proto +28 -0
- data/spec/schemas/simple/simple.proto +12 -0
- data/spec/spec_helper.rb +13 -0
- metadata +63 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d406d02e3db35efe7976a07238831d78ffeefc9692b6082c240aaf61983ec8be
|
4
|
+
data.tar.gz: 6ed547fb924c5d14526e22523417397b0b8b64682d4a79ca5aa78284ae940703
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8bd87219da56f1a9c555265dde48c7b11e86986b3e14ce4f849747054e7e5dc1c51ac64b43b584894f2215320580a72a9a159f0e94c56b868d99fc4c6ed0cc05
|
7
|
+
data.tar.gz: 43d9f128249dfc33e6f8db72a44541cc69a665e168075de1af6f40564e24c0bbdcc12587f1bdb4744ffe2606c3ff66cdc495ca08f06a6a7223c6882626d0b9be
|
@@ -0,0 +1,18 @@
|
|
1
|
+
name: Lint
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
|
8
|
+
runs-on: ubuntu-latest
|
9
|
+
|
10
|
+
steps:
|
11
|
+
- uses: actions/checkout@v3
|
12
|
+
- name: Set up Ruby ${{ matrix.ruby }}
|
13
|
+
uses: ruby/setup-ruby@v1
|
14
|
+
with:
|
15
|
+
ruby-version: 3.4
|
16
|
+
bundler-cache: true
|
17
|
+
- name: Run standardrb
|
18
|
+
run: bundle exec standardrb
|
@@ -0,0 +1,31 @@
|
|
1
|
+
name: Release Gem
|
2
|
+
on:
|
3
|
+
push:
|
4
|
+
branches:
|
5
|
+
- main
|
6
|
+
tags:
|
7
|
+
- 'v*.*.*' # Matches semantic versioning tags like v1.0.0
|
8
|
+
workflow_dispatch: # Allows manual triggering of the workflow
|
9
|
+
|
10
|
+
jobs:
|
11
|
+
push:
|
12
|
+
name: Push gem to RubyGems.org
|
13
|
+
runs-on: ubuntu-latest
|
14
|
+
|
15
|
+
permissions:
|
16
|
+
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
17
|
+
contents: write # IMPORTANT: this permission is required for `rake release` to push the release tag
|
18
|
+
|
19
|
+
steps:
|
20
|
+
# Set up
|
21
|
+
- uses: actions/checkout@v4
|
22
|
+
with:
|
23
|
+
persist-credentials: false
|
24
|
+
- name: Set up Ruby
|
25
|
+
uses: ruby/setup-ruby@v1
|
26
|
+
with:
|
27
|
+
bundler-cache: true
|
28
|
+
ruby-version: ruby
|
29
|
+
|
30
|
+
# Release
|
31
|
+
- uses: rubygems/release-gem@v1
|
@@ -0,0 +1,22 @@
|
|
1
|
+
name: Test
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
|
8
|
+
runs-on: ubuntu-latest
|
9
|
+
strategy:
|
10
|
+
fail-fast: false
|
11
|
+
matrix:
|
12
|
+
ruby: [3.1, 3.2, 3.3, 3.4]
|
13
|
+
|
14
|
+
steps:
|
15
|
+
- uses: actions/checkout@v3
|
16
|
+
- name: Set up Ruby ${{ matrix.ruby }}
|
17
|
+
uses: ruby/setup-ruby@v1
|
18
|
+
with:
|
19
|
+
ruby-version: ${{ matrix.ruby }}
|
20
|
+
bundler-cache: true
|
21
|
+
- name: Build and test with RSpec
|
22
|
+
run: bundle exec rspec
|
data/.rspec
ADDED
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## UNRELEASED
|
9
9
|
|
10
|
-
# 0.0.
|
10
|
+
# 0.0.4 - 2025-08-19
|
11
|
+
|
12
|
+
* Changed emitting of protobuf schemas from file-based to generated from descriptors.
|
13
|
+
|
14
|
+
# 0.0.3 - 2025-08-15
|
11
15
|
|
12
16
|
* Initial release.
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
proto_turf (0.0.4)
|
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
|
+
google-protobuf (4.30.2-x86_64-linux)
|
25
|
+
bigdecimal
|
26
|
+
rake (>= 13)
|
27
|
+
hashdiff (1.2.0)
|
28
|
+
json (2.13.2)
|
29
|
+
language_server-protocol (3.17.0.5)
|
30
|
+
lint_roller (1.1.0)
|
31
|
+
logger (1.7.0)
|
32
|
+
parallel (1.27.0)
|
33
|
+
parser (3.3.9.0)
|
34
|
+
ast (~> 2.4.1)
|
35
|
+
racc
|
36
|
+
prism (1.4.0)
|
37
|
+
public_suffix (6.0.2)
|
38
|
+
racc (1.8.1)
|
39
|
+
rainbow (3.1.1)
|
40
|
+
rake (13.3.0)
|
41
|
+
regexp_parser (2.11.2)
|
42
|
+
rexml (3.4.1)
|
43
|
+
rspec (3.13.0)
|
44
|
+
rspec-core (~> 3.13.0)
|
45
|
+
rspec-expectations (~> 3.13.0)
|
46
|
+
rspec-mocks (~> 3.13.0)
|
47
|
+
rspec-core (3.13.3)
|
48
|
+
rspec-support (~> 3.13.0)
|
49
|
+
rspec-expectations (3.13.4)
|
50
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
51
|
+
rspec-support (~> 3.13.0)
|
52
|
+
rspec-mocks (3.13.3)
|
53
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
54
|
+
rspec-support (~> 3.13.0)
|
55
|
+
rspec-support (3.13.3)
|
56
|
+
rubocop (1.75.8)
|
57
|
+
json (~> 2.3)
|
58
|
+
language_server-protocol (~> 3.17.0.2)
|
59
|
+
lint_roller (~> 1.1.0)
|
60
|
+
parallel (~> 1.10)
|
61
|
+
parser (>= 3.3.0.2)
|
62
|
+
rainbow (>= 2.2.2, < 4.0)
|
63
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
64
|
+
rubocop-ast (>= 1.44.0, < 2.0)
|
65
|
+
ruby-progressbar (~> 1.7)
|
66
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
67
|
+
rubocop-ast (1.46.0)
|
68
|
+
parser (>= 3.3.7.2)
|
69
|
+
prism (~> 1.4)
|
70
|
+
rubocop-performance (1.25.0)
|
71
|
+
lint_roller (~> 1.1)
|
72
|
+
rubocop (>= 1.75.0, < 2.0)
|
73
|
+
rubocop-ast (>= 1.38.0, < 2.0)
|
74
|
+
ruby-progressbar (1.13.0)
|
75
|
+
standard (1.50.0)
|
76
|
+
language_server-protocol (~> 3.17.0.2)
|
77
|
+
lint_roller (~> 1.0)
|
78
|
+
rubocop (~> 1.75.5)
|
79
|
+
standard-custom (~> 1.0.0)
|
80
|
+
standard-performance (~> 1.8)
|
81
|
+
standard-custom (1.0.2)
|
82
|
+
lint_roller (~> 1.0)
|
83
|
+
rubocop (~> 1.50)
|
84
|
+
standard-performance (1.8.0)
|
85
|
+
lint_roller (~> 1.1)
|
86
|
+
rubocop-performance (~> 1.25.0)
|
87
|
+
standardrb (1.0.1)
|
88
|
+
standard
|
89
|
+
unicode-display_width (3.1.5)
|
90
|
+
unicode-emoji (~> 4.0, >= 4.0.4)
|
91
|
+
unicode-emoji (4.0.4)
|
92
|
+
webmock (3.25.1)
|
93
|
+
addressable (>= 2.8.0)
|
94
|
+
crack (>= 0.3.2)
|
95
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
96
|
+
|
97
|
+
PLATFORMS
|
98
|
+
arm64-darwin
|
99
|
+
x86_64-linux
|
100
|
+
|
101
|
+
DEPENDENCIES
|
102
|
+
bundler (~> 2.0)
|
103
|
+
proto_turf!
|
104
|
+
rake (~> 13.0)
|
105
|
+
rspec (~> 3.2)
|
106
|
+
standardrb
|
107
|
+
webmock
|
108
|
+
|
109
|
+
BUNDLED WITH
|
110
|
+
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,7 +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
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.
|
44
46
|
|
47
|
+
### Regenerating test protos
|
48
|
+
Run the following to regenerate:
|
49
|
+
|
50
|
+
```sh
|
51
|
+
protoc -I spec/schemas --ruby_out=spec/gen --ruby_opt=paths=source_relative spec/schemas/**/*.proto
|
52
|
+
```
|
data/Rakefile
ADDED
@@ -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
|
@@ -0,0 +1,315 @@
|
|
1
|
+
class ProtoTurf
|
2
|
+
module ProtoText
|
3
|
+
ParseInfo = Struct.new(:writer, :package, :message) do
|
4
|
+
%i[write write_indent write_line writenl indent dedent].each do |method|
|
5
|
+
define_method(method) do |*args|
|
6
|
+
writer.send(method, *args)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Writer < StringIO
|
12
|
+
def initialize(...)
|
13
|
+
super
|
14
|
+
@indent = 0
|
15
|
+
end
|
16
|
+
|
17
|
+
def write_indent(str)
|
18
|
+
@indent.times { write(" ") }
|
19
|
+
write(str)
|
20
|
+
end
|
21
|
+
|
22
|
+
def write_line(line, nl = 1)
|
23
|
+
write_indent(line)
|
24
|
+
nl.times { writenl }
|
25
|
+
end
|
26
|
+
|
27
|
+
def writenl
|
28
|
+
write("\n")
|
29
|
+
end
|
30
|
+
|
31
|
+
def indent
|
32
|
+
@indent += 2
|
33
|
+
end
|
34
|
+
|
35
|
+
def dedent
|
36
|
+
@indent -= 2
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class << self
|
41
|
+
def fetch(message_name)
|
42
|
+
name = message_name.start_with?(".") ? message_name[1..] : message_name
|
43
|
+
Google::Protobuf::DescriptorPool.generated_pool.lookup(name)
|
44
|
+
end
|
45
|
+
|
46
|
+
def output(file_descriptor)
|
47
|
+
writer = Writer.new
|
48
|
+
info = ParseInfo.new(writer, file_descriptor.package)
|
49
|
+
writer.write_line("syntax = \"#{file_descriptor.syntax}\";", 2)
|
50
|
+
writer.write_line("package #{file_descriptor.package};")
|
51
|
+
writer.writenl
|
52
|
+
found = false
|
53
|
+
file_descriptor.options.to_h.each do |name, value|
|
54
|
+
found = true
|
55
|
+
writer.write_line("option #{name} = #{value.to_json};")
|
56
|
+
end
|
57
|
+
writer.writenl if found
|
58
|
+
|
59
|
+
found = false
|
60
|
+
file_descriptor.dependency.each do |dependency|
|
61
|
+
found = true
|
62
|
+
writer.write_line("import \"#{dependency}\";")
|
63
|
+
end
|
64
|
+
writer.writenl if found
|
65
|
+
|
66
|
+
writer.writenl if write_options(info, file_descriptor)
|
67
|
+
writer.writenl if write_extensions(info, file_descriptor)
|
68
|
+
|
69
|
+
file_descriptor.enum_type.each do |enum_type|
|
70
|
+
write_enum(info, enum_type)
|
71
|
+
end
|
72
|
+
file_descriptor.message_type.each do |message_type|
|
73
|
+
write_message(info, message_type)
|
74
|
+
end
|
75
|
+
file_descriptor.service.each do |service|
|
76
|
+
write_service(info, service)
|
77
|
+
end
|
78
|
+
writer.string
|
79
|
+
end
|
80
|
+
|
81
|
+
def write_extensions(info, descriptor)
|
82
|
+
descriptor.extension.each do |extension|
|
83
|
+
info.write_line("extend #{extension.extendee[1..]} {")
|
84
|
+
info.indent
|
85
|
+
write_field(info, extension)
|
86
|
+
info.dedent
|
87
|
+
info.write_line("}")
|
88
|
+
end
|
89
|
+
descriptor.extension.any?
|
90
|
+
end
|
91
|
+
|
92
|
+
def write_reserved(writer, descriptor)
|
93
|
+
reserved = descriptor.reserved_range.map do |range|
|
94
|
+
(range.start == range.end - 1) ? range.start.to_s : "#{range.start} to #{range.end - 1}"
|
95
|
+
end
|
96
|
+
found = false
|
97
|
+
if reserved.any?
|
98
|
+
found = true
|
99
|
+
writer.write_line("reserved #{reserved.join(", ")};")
|
100
|
+
end
|
101
|
+
if descriptor.reserved_name.any?
|
102
|
+
found = true
|
103
|
+
writer.write_line("reserved #{descriptor.reserved_name.map(&:to_json).join(", ")};")
|
104
|
+
end
|
105
|
+
writer.writenl if found
|
106
|
+
end
|
107
|
+
|
108
|
+
def write_imports(writer, file_descriptor)
|
109
|
+
writer.writenl
|
110
|
+
file_descriptor.dependency.each do |dependency|
|
111
|
+
writer.write_line("import \"#{dependency}\";")
|
112
|
+
end
|
113
|
+
file_descriptor.public_dependency.each do |public_dependency|
|
114
|
+
writer.write_line("import public \"#{public_dependency}\";")
|
115
|
+
end
|
116
|
+
file_descriptor.option_dependency.each do |option_dependency|
|
117
|
+
writer.write_line("import weak \"#{option_dependency}\";")
|
118
|
+
end
|
119
|
+
writer.writenl
|
120
|
+
end
|
121
|
+
|
122
|
+
def write_message(info, message_type)
|
123
|
+
info.message = message_type
|
124
|
+
info.write_indent("message ")
|
125
|
+
info.write("#{message_type.name} {")
|
126
|
+
info.writenl
|
127
|
+
info.indent
|
128
|
+
|
129
|
+
write_options(info, message_type)
|
130
|
+
write_reserved(info, message_type)
|
131
|
+
|
132
|
+
message_type.enum_type.each do |enum|
|
133
|
+
info.writenl
|
134
|
+
write_enum(info, enum)
|
135
|
+
end
|
136
|
+
message_type.field.each do |field|
|
137
|
+
write_field(info, field)
|
138
|
+
end
|
139
|
+
message_type.extension.each do |extension|
|
140
|
+
write_field(info, extension)
|
141
|
+
end
|
142
|
+
write_oneofs(info, message_type)
|
143
|
+
message_type.nested_type.each do |subtype|
|
144
|
+
info.writenl
|
145
|
+
write_message(info, subtype)
|
146
|
+
end
|
147
|
+
info.dedent
|
148
|
+
info.write_line("}")
|
149
|
+
end
|
150
|
+
|
151
|
+
def field_type(info, field_type)
|
152
|
+
case field_type.type
|
153
|
+
when :TYPE_INT32
|
154
|
+
"int32"
|
155
|
+
when :TYPE_INT64
|
156
|
+
"int64"
|
157
|
+
when :TYPE_UINT32
|
158
|
+
"uint32"
|
159
|
+
when :TYPE_UINT64
|
160
|
+
"uint64"
|
161
|
+
when :TYPE_SINT32
|
162
|
+
"sint32"
|
163
|
+
when :TYPE_SINT64
|
164
|
+
"sint64"
|
165
|
+
when :TYPE_FIXED32
|
166
|
+
"fixed32"
|
167
|
+
when :TYPE_FIXED64
|
168
|
+
"fixed64"
|
169
|
+
when :TYPE_SFIXED32
|
170
|
+
"sfixed32"
|
171
|
+
when :TYPE_SFIXED64
|
172
|
+
"sfixed64"
|
173
|
+
when :TYPE_FLOAT
|
174
|
+
"float"
|
175
|
+
when :TYPE_DOUBLE
|
176
|
+
"double"
|
177
|
+
when :TYPE_BOOL
|
178
|
+
"bool"
|
179
|
+
when :TYPE_STRING
|
180
|
+
"string"
|
181
|
+
when :TYPE_BYTES
|
182
|
+
"bytes"
|
183
|
+
when :TYPE_ENUM, :TYPE_MESSAGE
|
184
|
+
# remove leading .
|
185
|
+
type = fetch(field_type.type_name[1..])
|
186
|
+
name = type.name.sub("#{info.package}.#{info.message.name}.", "")
|
187
|
+
name.sub("#{info.package}.", "")
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def write_field(info, field, oneof: false)
|
192
|
+
info.write_indent("")
|
193
|
+
return if !oneof && field.has_oneof_index?
|
194
|
+
|
195
|
+
klass = nil
|
196
|
+
if field.type_name && field.type_name != ""
|
197
|
+
klass = fetch(field.type_name).to_proto
|
198
|
+
end
|
199
|
+
|
200
|
+
if field.proto3_optional
|
201
|
+
info.write("optional ")
|
202
|
+
elsif field.label == :LABEL_REPEATED && !klass&.options&.map_entry
|
203
|
+
info.write("repeated ")
|
204
|
+
end
|
205
|
+
|
206
|
+
if klass&.options&.map_entry
|
207
|
+
info.write("map<#{field_type(info, klass.field[0])}, #{field_type(info, klass.field[1])}>")
|
208
|
+
else
|
209
|
+
info.write(field_type(info, field).to_s)
|
210
|
+
end
|
211
|
+
info.write(" #{field.name} = #{field.number}")
|
212
|
+
|
213
|
+
write_field_options(info, field)
|
214
|
+
info.write(";")
|
215
|
+
info.writenl
|
216
|
+
end
|
217
|
+
|
218
|
+
def write_field_options(info, field)
|
219
|
+
return unless field.options
|
220
|
+
|
221
|
+
info.write(" [")
|
222
|
+
info.write(field.options.to_h.map { |name, value| "#{name} = #{value}" }.join(", "))
|
223
|
+
write_options(info, field, include_option_label: false)
|
224
|
+
info.write("]")
|
225
|
+
end
|
226
|
+
|
227
|
+
def write_oneofs(info, message)
|
228
|
+
message.oneof_decl.each_with_index do |oneof, i|
|
229
|
+
next if oneof.name.start_with?("_") &&
|
230
|
+
message.field.any? { |f| f.proto3_optional && f.name == oneof.name[1..] }
|
231
|
+
info.write_line("oneof #{oneof.name} {")
|
232
|
+
info.indent
|
233
|
+
message.field.select { |f| f.has_oneof_index? && f.oneof_index == i }.each do |field|
|
234
|
+
write_field(info, field, oneof: true)
|
235
|
+
end
|
236
|
+
info.dedent
|
237
|
+
info.write_line("}")
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def write_enum(info, enum_type)
|
242
|
+
info.write("enum ")
|
243
|
+
info.write("#{enum_type.name} {")
|
244
|
+
info.writenl
|
245
|
+
info.indent
|
246
|
+
write_reserved(info, enum_type)
|
247
|
+
enum_type.value.each do |value|
|
248
|
+
info.write_line("#{value.name} = #{value.number};")
|
249
|
+
end
|
250
|
+
info.dedent
|
251
|
+
info.write_line("}")
|
252
|
+
info.writenl
|
253
|
+
end
|
254
|
+
|
255
|
+
def method_type(package, name)
|
256
|
+
output = name.sub("#{package}.", "")
|
257
|
+
output = output[1..] if output.start_with?(".")
|
258
|
+
output
|
259
|
+
end
|
260
|
+
|
261
|
+
def write_service(info, service)
|
262
|
+
info.write_line("service #{service.name} {")
|
263
|
+
info.indent
|
264
|
+
service["method"].each do |method|
|
265
|
+
info.write_indent("rpc #{method.name}(#{method_type(info.package, method.input_type)}) ")
|
266
|
+
info.write("returns (#{method_type(info.package, method.output_type)}) {")
|
267
|
+
info.writenl
|
268
|
+
info.indent
|
269
|
+
if method.options
|
270
|
+
write_options(info, method)
|
271
|
+
end
|
272
|
+
info.dedent
|
273
|
+
info.write_line("};")
|
274
|
+
end
|
275
|
+
info.dedent
|
276
|
+
info.write_line("}")
|
277
|
+
end
|
278
|
+
|
279
|
+
# @return [Boolean] true if any options were written
|
280
|
+
def write_options(info, descriptor, include_option_label: true)
|
281
|
+
# unfortunately there doesn't seem to be a way to get the full list of options without
|
282
|
+
# resorting to to_json.
|
283
|
+
json = JSON.parse(descriptor.options.to_json)
|
284
|
+
return if !json || json.empty?
|
285
|
+
|
286
|
+
found = false
|
287
|
+
json.keys.each do |name|
|
288
|
+
option_name = name.tr("[]", "")
|
289
|
+
ext = fetch(option_name)
|
290
|
+
next if ext.nil?
|
291
|
+
|
292
|
+
found = true
|
293
|
+
options = ext.get(descriptor.options)
|
294
|
+
if include_option_label
|
295
|
+
info.write_indent("option (#{option_name}) =")
|
296
|
+
else
|
297
|
+
info.write("(#{option_name}) = ")
|
298
|
+
end
|
299
|
+
if options.respond_to?(:to_h)
|
300
|
+
lines = JSON.pretty_generate(options.to_h).lines(chomp: true)
|
301
|
+
lines.each_with_index do |line, i|
|
302
|
+
info.write_indent(line)
|
303
|
+
info.writenl if i < lines.length - 1
|
304
|
+
end
|
305
|
+
info.write(";")
|
306
|
+
else
|
307
|
+
info.write(options.to_json)
|
308
|
+
end
|
309
|
+
info.writenl
|
310
|
+
end
|
311
|
+
found
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
data/lib/proto_turf/version.rb
CHANGED
data/lib/proto_turf.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
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"
|
8
|
+
require "proto_turf/proto_text"
|
4
9
|
|
5
10
|
class ProtoTurf
|
6
11
|
# Provides a way to encode and decode messages without having to embed schemas
|
@@ -15,14 +20,12 @@ class ProtoTurf
|
|
15
20
|
# https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format
|
16
21
|
MAGIC_BYTE = [0].pack("C").freeze
|
17
22
|
|
18
|
-
|
19
23
|
# Instantiate a new ProtoTurf instance with the given configuration.
|
20
24
|
#
|
21
25
|
# registry - A schema registry object that responds to all methods in the
|
22
26
|
# ProtoTurf::ConfluentSchemaRegistry interface.
|
23
27
|
# registry_url - The String URL of the schema registry that should be used.
|
24
28
|
# schema_context - Schema registry context name (optional)
|
25
|
-
# schemas_path - 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,6 @@ class ProtoTurf
|
|
40
43
|
registry: nil,
|
41
44
|
registry_url: nil,
|
42
45
|
schema_context: nil,
|
43
|
-
schemas_path: nil,
|
44
46
|
registry_path_prefix: nil,
|
45
47
|
logger: nil,
|
46
48
|
proxy: nil,
|
@@ -57,7 +59,6 @@ class ProtoTurf
|
|
57
59
|
retry_limit: nil
|
58
60
|
)
|
59
61
|
@logger = logger || Logger.new($stderr)
|
60
|
-
@path = schemas_path
|
61
62
|
@registry = registry || ProtoTurf::CachedConfluentSchemaRegistry.new(
|
62
63
|
ProtoTurf::ConfluentSchemaRegistry.new(
|
63
64
|
registry_url,
|
@@ -101,7 +102,7 @@ class ProtoTurf
|
|
101
102
|
stream.write([id].pack("N"))
|
102
103
|
|
103
104
|
_, indexes = find_index(message.class.descriptor.to_proto,
|
104
|
-
|
105
|
+
file_descriptor.to_proto.message_type)
|
105
106
|
|
106
107
|
if indexes == [0]
|
107
108
|
write_int(stream, 0)
|
@@ -138,7 +139,7 @@ class ProtoTurf
|
|
138
139
|
end
|
139
140
|
|
140
141
|
# The schema id is a 4-byte big-endian integer.
|
141
|
-
schema_id = stream.read(4).
|
142
|
+
schema_id = stream.read(4).unpack1("N")
|
142
143
|
|
143
144
|
# For now, we're only going to support a single message per schema. See
|
144
145
|
# https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format
|
@@ -161,7 +162,7 @@ class ProtoTurf
|
|
161
162
|
|
162
163
|
private
|
163
164
|
|
164
|
-
def find_index(descriptor, messages, indexes=[])
|
165
|
+
def find_index(descriptor, messages, indexes = [])
|
165
166
|
messages.each_with_index do |sub_descriptor, i|
|
166
167
|
if sub_descriptor == descriptor
|
167
168
|
indexes.push(i)
|
@@ -221,7 +222,7 @@ class ProtoTurf
|
|
221
222
|
raise "Could not find schema for #{full_name}. Make sure the corresponding .proto file has been compiled and loaded."
|
222
223
|
end
|
223
224
|
path = find_descriptor(indexes, descriptor.file_descriptor.to_proto.message_type)
|
224
|
-
correct_message = Google::Protobuf::DescriptorPool.generated_pool.lookup("#{package}.#{path.join(
|
225
|
+
correct_message = Google::Protobuf::DescriptorPool.generated_pool.lookup("#{package}.#{path.join(".")}")
|
225
226
|
correct_message.msgclass.decode(encoded)
|
226
227
|
end
|
227
228
|
|
@@ -230,7 +231,7 @@ class ProtoTurf
|
|
230
231
|
return if @registry.registered?(file_descriptor.name, subject)
|
231
232
|
|
232
233
|
# register dependencies first
|
233
|
-
dependencies = file_descriptor.to_proto.dependency.to_a.reject { |d| d.start_with?(
|
234
|
+
dependencies = file_descriptor.to_proto.dependency.to_a.reject { |d| d.start_with?("google/protobuf/") }
|
234
235
|
versions = dependencies.map do |dependency|
|
235
236
|
dependency_descriptor = @all_schemas[dependency]
|
236
237
|
result = register_schema(dependency_descriptor, subject: dependency_descriptor.name)
|
@@ -238,33 +239,27 @@ class ProtoTurf
|
|
238
239
|
end
|
239
240
|
|
240
241
|
@registry.register(subject,
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
)
|
242
|
+
schema_text(file_descriptor),
|
243
|
+
references: dependencies.map.with_index do |dependency, i|
|
244
|
+
{
|
245
|
+
name: dependency,
|
246
|
+
subject: dependency,
|
247
|
+
version: versions[i]
|
248
|
+
}
|
249
|
+
end)
|
250
250
|
end
|
251
251
|
|
252
252
|
def schema_text(file_descriptor)
|
253
|
-
|
254
|
-
File.exist?(filename) ? File.read(filename) : ""
|
253
|
+
ProtoTurf::ProtoText.output(file_descriptor.to_proto)
|
255
254
|
end
|
256
255
|
|
257
256
|
def load_schemas!
|
258
|
-
|
259
|
-
|
260
|
-
end.to_a
|
261
|
-
all_messages.each do |m|
|
262
|
-
file_desc = m.descriptor.file_descriptor
|
257
|
+
all_files = ObjectSpace.each_object(Google::Protobuf::FileDescriptor).to_a
|
258
|
+
all_files.each do |file_desc|
|
263
259
|
file_path = file_desc.name
|
264
|
-
next if file_path.start_with?(
|
260
|
+
next if file_path.start_with?("google/protobuf/") # skip built-in protos
|
265
261
|
|
266
262
|
@all_schemas[file_path] = file_desc
|
267
263
|
end
|
268
264
|
end
|
269
|
-
|
270
265
|
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,36 @@
|
|
1
|
+
RSpec.describe "encoding" do
|
2
|
+
let(:proto_turf) do
|
3
|
+
ProtoTurf.new(
|
4
|
+
registry_url: "http://localhost:8081"
|
5
|
+
)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should decode a simple message" do
|
9
|
+
schema = File.read("#{__dir__}/schemas/simple/simple.proto")
|
10
|
+
stub = stub_request(:get, "http://localhost:8081/schemas/ids/15")
|
11
|
+
.to_return_json(body: {schema: schema})
|
12
|
+
msg = Simple::V1::SimpleMessage.new(name: "my name")
|
13
|
+
encoded = "\u0000\u0000\u0000\u0000\u000F\u0000" + msg.to_proto
|
14
|
+
expect(proto_turf.decode(encoded)).to eq(msg)
|
15
|
+
|
16
|
+
# if we do it again we should not see any more requests
|
17
|
+
expect(proto_turf.decode(encoded)).to eq(msg)
|
18
|
+
|
19
|
+
expect(stub).to have_been_requested.once
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should decode a complex message" do
|
23
|
+
schema = File.read("#{__dir__}/schemas/referenced/referer.proto")
|
24
|
+
stub = stub_request(:get, "http://localhost:8081/schemas/ids/20")
|
25
|
+
.to_return_json(body: {schema: schema})
|
26
|
+
msg = Referenced::V1::MessageB::MessageBA.new(
|
27
|
+
simple: Simple::V1::SimpleMessage.new(name: "my name")
|
28
|
+
)
|
29
|
+
encoded = "\u0000\u0000\u0000\u0000\u0014\u0004\u0002\u0000" + msg.to_proto
|
30
|
+
expect(proto_turf.decode(encoded)).to eq(msg)
|
31
|
+
|
32
|
+
# if we do it again we should not see any more requests
|
33
|
+
expect(proto_turf.decode(encoded)).to eq(msg)
|
34
|
+
expect(stub).to have_been_requested.once
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
RSpec.describe "encoding" do
|
2
|
+
let(:proto_turf) do
|
3
|
+
ProtoTurf.new(
|
4
|
+
registry_url: "http://localhost:8081"
|
5
|
+
)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should encode a simple message" do
|
9
|
+
schema = File.read("#{__dir__}/schemas/simple/simple.proto")
|
10
|
+
stub = stub_request(:post, "http://localhost:8081/subjects/simple/versions")
|
11
|
+
.with(body: {"schemaType" => "PROTOBUF",
|
12
|
+
"references" => [],
|
13
|
+
"schema" => schema}).to_return_json(body: {id: 15})
|
14
|
+
msg = Simple::V1::SimpleMessage.new(name: "my name")
|
15
|
+
encoded = proto_turf.encode(msg, subject: "simple")
|
16
|
+
expect(encoded).to eq("\u0000\u0000\u0000\u0000\u000F\u0000" + msg.to_proto)
|
17
|
+
|
18
|
+
# if we do it again we should not see any more requests
|
19
|
+
encoded2 = proto_turf.encode(msg, subject: "simple")
|
20
|
+
expect(encoded2).to eq(encoded)
|
21
|
+
|
22
|
+
expect(stub).to have_been_requested.once
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should encode a complex message" do
|
26
|
+
schema = File.read("#{__dir__}/schemas/referenced/referer.proto")
|
27
|
+
dep_schema = File.read("#{__dir__}/schemas/simple/simple.proto")
|
28
|
+
dep_stub = stub_request(:post, "http://localhost:8081/subjects/simple%2Fsimple.proto/versions")
|
29
|
+
.with(body: {"schemaType" => "PROTOBUF",
|
30
|
+
"references" => [],
|
31
|
+
"schema" => dep_schema}).to_return_json(body: {id: 15})
|
32
|
+
version_stub = stub_request(:get, "http://localhost:8081/schemas/ids/15/versions")
|
33
|
+
.to_return_json(body: [{version: 1, subject: "simple/simple.proto"}])
|
34
|
+
stub = stub_request(:post, "http://localhost:8081/subjects/referenced/versions")
|
35
|
+
.with(body: {"schemaType" => "PROTOBUF",
|
36
|
+
"references" => [
|
37
|
+
{
|
38
|
+
name: "simple/simple.proto",
|
39
|
+
subject: "simple/simple.proto",
|
40
|
+
version: 1
|
41
|
+
}
|
42
|
+
],
|
43
|
+
"schema" => schema}).to_return_json(body: {id: 20})
|
44
|
+
msg = Referenced::V1::MessageB::MessageBA.new(
|
45
|
+
simple: Simple::V1::SimpleMessage.new(name: "my name")
|
46
|
+
)
|
47
|
+
encoded = proto_turf.encode(msg, subject: "referenced")
|
48
|
+
expect(encoded).to eq("\u0000\u0000\u0000\u0000\u0014\u0004\u0002\u0000" + msg.to_proto)
|
49
|
+
|
50
|
+
# if we do it again we should not see any more requests
|
51
|
+
encoded2 = proto_turf.encode(msg, subject: "referenced")
|
52
|
+
expect(encoded2).to eq(encoded)
|
53
|
+
expect(stub).to have_been_requested.once
|
54
|
+
expect(dep_stub).to have_been_requested.once
|
55
|
+
expect(version_stub).to have_been_requested.once
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
4
|
+
# source: everything/everything.proto
|
5
|
+
|
6
|
+
require "google/protobuf"
|
7
|
+
|
8
|
+
require "simple/simple_pb"
|
9
|
+
require "google/protobuf/descriptor_pb"
|
10
|
+
|
11
|
+
descriptor_data = "\n\x1b\x65verything/everything.proto\x12\reverything.v1\x1a\x13simple/simple.proto\x1a google/protobuf/descriptor.proto\"*\n\x0e\x46oreignMessage\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0c\n\x04name\x18\x02 \x01(\t\"\xc0\x12\n\x0cTestAllTypes\x12\x16\n\x0eoptional_int32\x18\x01 \x01(\x05\x12\x16\n\x0eoptional_int64\x18\x02 \x01(\x03\x12\x17\n\x0foptional_uint32\x18\x03 \x01(\r\x12\x17\n\x0foptional_uint64\x18\x04 \x01(\x04\x12\x17\n\x0foptional_sint32\x18\x05 \x01(\x11\x12\x17\n\x0foptional_sint64\x18\x06 \x01(\x12\x12\x18\n\x10optional_fixed32\x18\x07 \x01(\x07\x12\x18\n\x10optional_fixed64\x18\x08 \x01(\x06\x12\x19\n\x11optional_sfixed32\x18\t \x01(\x0f\x12\x19\n\x11optional_sfixed64\x18\n \x01(\x10\x12\x16\n\x0eoptional_float\x18\x0b \x01(\x02\x12\x17\n\x0foptional_double\x18\x0c \x01(\x01\x12\x15\n\roptional_bool\x18\r \x01(\x08\x12\x17\n\x0foptional_string\x18\x0e \x01(\t\x12\x16\n\x0eoptional_bytes\x18\x0f \x01(\x0c\x12J\n\x17optional_nested_message\x18\x12 \x01(\x0b\x32).everything.v1.TestAllTypes.NestedMessage\x12?\n\x18optional_foreign_message\x18\x13 \x01(\x0b\x32\x1d.everything.v1.ForeignMessage\x12\x39\n\x17optional_import_message\x18\x14 \x01(\x0b\x32\x18.simple.v1.SimpleMessage\x12\x44\n\x14optional_nested_enum\x18\x15 \x01(\x0e\x32&.everything.v1.TestAllTypes.NestedEnum\x12\x39\n\x15optional_foreign_enum\x18\x16 \x01(\x0e\x32\x1a.everything.v1.ForeignEnum\x12\x33\n\x14optional_import_enum\x18\x17 \x01(\x0e\x32\x15.simple.v1.SimpleEnum\x12!\n\x15optional_string_piece\x18\x18 \x01(\tB\x02\x08\x02\x12\x19\n\roptional_cord\x18\x19 \x01(\tB\x02\x08\x01\x12\x1f\n\x13optional_bytes_cord\x18V \x01(\x0c\x42\x02\x08\x01\x12L\n\x15optional_lazy_message\x18\x1b \x01(\x0b\x32).everything.v1.TestAllTypes.NestedMessageB\x02(\x01\x12W\n optional_unverified_lazy_message\x18\x1c \x01(\x0b\x32).everything.v1.TestAllTypes.NestedMessageB\x02x\x01\x12\x16\n\x0erepeated_int32\x18\x1f \x03(\x05\x12\x16\n\x0erepeated_int64\x18 \x03(\x03\x12\x17\n\x0frepeated_uint32\x18! \x03(\r\x12\x17\n\x0frepeated_uint64\x18\" \x03(\x04\x12\x17\n\x0frepeated_sint32\x18# \x03(\x11\x12\x17\n\x0frepeated_sint64\x18$ \x03(\x12\x12\x18\n\x10repeated_fixed32\x18% \x03(\x07\x12\x18\n\x10repeated_fixed64\x18& \x03(\x06\x12\x19\n\x11repeated_sfixed32\x18' \x03(\x0f\x12\x19\n\x11repeated_sfixed64\x18( \x03(\x10\x12\x16\n\x0erepeated_float\x18) \x03(\x02\x12\x17\n\x0frepeated_double\x18* \x03(\x01\x12\x15\n\rrepeated_bool\x18+ \x03(\x08\x12\x17\n\x0frepeated_string\x18, \x03(\t\x12\x16\n\x0erepeated_bytes\x18- \x03(\x0c\x12J\n\x17repeated_nested_message\x18\x30 \x03(\x0b\x32).everything.v1.TestAllTypes.NestedMessage\x12?\n\x18repeated_foreign_message\x18\x31 \x03(\x0b\x32\x1d.everything.v1.ForeignMessage\x12\x39\n\x17repeated_import_message\x18\x32 \x03(\x0b\x32\x18.simple.v1.SimpleMessage\x12\x44\n\x14repeated_nested_enum\x18\x33 \x03(\x0e\x32&.everything.v1.TestAllTypes.NestedEnum\x12\x39\n\x15repeated_foreign_enum\x18\x34 \x03(\x0e\x32\x1a.everything.v1.ForeignEnum\x12\x33\n\x14repeated_import_enum\x18\x35 \x03(\x0e\x32\x15.simple.v1.SimpleEnum\x12!\n\x15repeated_string_piece\x18\x36 \x03(\tB\x02\x08\x02\x12\x19\n\rrepeated_cord\x18\x37 \x03(\tB\x02\x08\x01\x12L\n\x15repeated_lazy_message\x18\x39 \x03(\x0b\x32).everything.v1.TestAllTypes.NestedMessageB\x02(\x01\x12\x16\n\x0coneof_uint32\x18o \x01(\rH\x00\x12I\n\x14oneof_nested_message\x18p \x01(\x0b\x32).everything.v1.TestAllTypes.NestedMessageH\x00\x12\x16\n\x0coneof_string\x18q \x01(\tH\x00\x12\x15\n\x0boneof_bytes\x18r \x01(\x0cH\x00\x12\x18\n\noneof_cord\x18s \x01(\tB\x02\x08\x01H\x00\x12 \n\x12oneof_string_piece\x18t \x01(\tB\x02\x08\x02H\x00\x12R\n\x19oneof_lazy_nested_message\x18u \x01(\x0b\x32).everything.v1.TestAllTypes.NestedMessageB\x02(\x01H\x00\x1a\"\n\rNestedMessage\x12\x11\n\x02\x62\x62\x18\x01 \x01(\x05\x42\x05\x90\x82\x19\xd2\t\x1a\x1a\n\rOptionalGroup\x12\t\n\x01\x61\x18\x11 \x01(\x05\x1a\x1a\n\rRepeatedGroup\x12\t\n\x01\x61\x18/ \x01(\x05\"'\n\nNestedEnum\x12\x07\n\x03\x46OO\x10\x00\x12\x07\n\x03\x42\x41R\x10\x02\x12\x07\n\x03\x42\x41Z\x10\x03\x42\r\n\x0boneof_field*/\n\x0b\x46oreignEnum\x12\x0f\n\x0b\x46OREIGN_FOO\x10\x00\x12\x0f\n\x0b\x46OREIGN_BAR\x10\x01\x32Y\n\x0bTestService\x12J\n\nTestMethod\x12\x1b.everything.v1.TestAllTypes\x1a\x1d.everything.v1.ForeignMessage\"\x00:4\n\x0bsome_option\x12\x1d.google.protobuf.FieldOptions\x18\xa2\x90\x03 \x01(\x05\x62\x06proto3"
|
12
|
+
|
13
|
+
pool = Google::Protobuf::DescriptorPool.generated_pool
|
14
|
+
pool.add_serialized_file(descriptor_data)
|
15
|
+
|
16
|
+
module Everything
|
17
|
+
module V1
|
18
|
+
ForeignMessage = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("everything.v1.ForeignMessage").msgclass
|
19
|
+
TestAllTypes = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("everything.v1.TestAllTypes").msgclass
|
20
|
+
TestAllTypes::NestedMessage = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("everything.v1.TestAllTypes.NestedMessage").msgclass
|
21
|
+
TestAllTypes::OptionalGroup = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("everything.v1.TestAllTypes.OptionalGroup").msgclass
|
22
|
+
TestAllTypes::RepeatedGroup = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("everything.v1.TestAllTypes.RepeatedGroup").msgclass
|
23
|
+
TestAllTypes::NestedEnum = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("everything.v1.TestAllTypes.NestedEnum").enummodule
|
24
|
+
ForeignEnum = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("everything.v1.ForeignEnum").enummodule
|
25
|
+
end
|
26
|
+
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,18 @@
|
|
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(\t*,\n\nSimpleEnum\x12\x0e\n\nSIMPLE_FOO\x10\x00\x12\x0e\n\nSIMPLE_BAR\x10\x01\x62\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
|
+
SimpleEnum = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("simple.v1.SimpleEnum").enummodule
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
RSpec.describe ProtoTurf::ProtoText do
|
2
|
+
it "should output as expected" do
|
3
|
+
output = described_class.output(Everything::V1::TestAllTypes.descriptor.file_descriptor.to_proto)
|
4
|
+
|
5
|
+
expected = File.read("#{__dir__}/schemas/everything/everything.proto")
|
6
|
+
expect(output).to eq(expected)
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
syntax = "proto3";
|
2
|
+
|
3
|
+
package everything.v1;
|
4
|
+
|
5
|
+
import "simple/simple.proto";
|
6
|
+
import "google/protobuf/descriptor.proto";
|
7
|
+
|
8
|
+
extend google.protobuf.FieldOptions {
|
9
|
+
int32 some_option = 51234;
|
10
|
+
}
|
11
|
+
|
12
|
+
enum ForeignEnum {
|
13
|
+
FOREIGN_FOO = 0;
|
14
|
+
FOREIGN_BAR = 1;
|
15
|
+
}
|
16
|
+
|
17
|
+
message ForeignMessage {
|
18
|
+
int32 id = 1;
|
19
|
+
string name = 2;
|
20
|
+
}
|
21
|
+
message TestAllTypes {
|
22
|
+
|
23
|
+
enum NestedEnum {
|
24
|
+
FOO = 0;
|
25
|
+
BAR = 2;
|
26
|
+
BAZ = 3;
|
27
|
+
}
|
28
|
+
|
29
|
+
int32 optional_int32 = 1;
|
30
|
+
int64 optional_int64 = 2;
|
31
|
+
uint32 optional_uint32 = 3;
|
32
|
+
uint64 optional_uint64 = 4;
|
33
|
+
sint32 optional_sint32 = 5;
|
34
|
+
sint64 optional_sint64 = 6;
|
35
|
+
fixed32 optional_fixed32 = 7;
|
36
|
+
fixed64 optional_fixed64 = 8;
|
37
|
+
sfixed32 optional_sfixed32 = 9;
|
38
|
+
sfixed64 optional_sfixed64 = 10;
|
39
|
+
float optional_float = 11;
|
40
|
+
double optional_double = 12;
|
41
|
+
bool optional_bool = 13;
|
42
|
+
string optional_string = 14;
|
43
|
+
bytes optional_bytes = 15;
|
44
|
+
NestedMessage optional_nested_message = 18;
|
45
|
+
ForeignMessage optional_foreign_message = 19;
|
46
|
+
simple.v1.SimpleMessage optional_import_message = 20;
|
47
|
+
NestedEnum optional_nested_enum = 21;
|
48
|
+
ForeignEnum optional_foreign_enum = 22;
|
49
|
+
simple.v1.SimpleEnum optional_import_enum = 23;
|
50
|
+
string optional_string_piece = 24 [ctype = STRING_PIECE];
|
51
|
+
string optional_cord = 25 [ctype = CORD];
|
52
|
+
bytes optional_bytes_cord = 86 [ctype = CORD];
|
53
|
+
NestedMessage optional_lazy_message = 27 [lazy = true];
|
54
|
+
NestedMessage optional_unverified_lazy_message = 28 [unverified_lazy = true];
|
55
|
+
repeated int32 repeated_int32 = 31;
|
56
|
+
repeated int64 repeated_int64 = 32;
|
57
|
+
repeated uint32 repeated_uint32 = 33;
|
58
|
+
repeated uint64 repeated_uint64 = 34;
|
59
|
+
repeated sint32 repeated_sint32 = 35;
|
60
|
+
repeated sint64 repeated_sint64 = 36;
|
61
|
+
repeated fixed32 repeated_fixed32 = 37;
|
62
|
+
repeated fixed64 repeated_fixed64 = 38;
|
63
|
+
repeated sfixed32 repeated_sfixed32 = 39;
|
64
|
+
repeated sfixed64 repeated_sfixed64 = 40;
|
65
|
+
repeated float repeated_float = 41;
|
66
|
+
repeated double repeated_double = 42;
|
67
|
+
repeated bool repeated_bool = 43;
|
68
|
+
repeated string repeated_string = 44;
|
69
|
+
repeated bytes repeated_bytes = 45;
|
70
|
+
repeated NestedMessage repeated_nested_message = 48;
|
71
|
+
repeated ForeignMessage repeated_foreign_message = 49;
|
72
|
+
repeated simple.v1.SimpleMessage repeated_import_message = 50;
|
73
|
+
repeated NestedEnum repeated_nested_enum = 51;
|
74
|
+
repeated ForeignEnum repeated_foreign_enum = 52;
|
75
|
+
repeated simple.v1.SimpleEnum repeated_import_enum = 53;
|
76
|
+
repeated string repeated_string_piece = 54 [ctype = STRING_PIECE];
|
77
|
+
repeated string repeated_cord = 55 [ctype = CORD];
|
78
|
+
repeated NestedMessage repeated_lazy_message = 57 [lazy = true];
|
79
|
+
oneof oneof_field {
|
80
|
+
uint32 oneof_uint32 = 111;
|
81
|
+
NestedMessage oneof_nested_message = 112;
|
82
|
+
string oneof_string = 113;
|
83
|
+
bytes oneof_bytes = 114;
|
84
|
+
string oneof_cord = 115 [ctype = CORD];
|
85
|
+
string oneof_string_piece = 116 [ctype = STRING_PIECE];
|
86
|
+
NestedMessage oneof_lazy_nested_message = 117 [lazy = true];
|
87
|
+
}
|
88
|
+
|
89
|
+
message NestedMessage {
|
90
|
+
int32 bb = 1 [(everything.v1.some_option) = 1234
|
91
|
+
];
|
92
|
+
}
|
93
|
+
|
94
|
+
message OptionalGroup {
|
95
|
+
int32 a = 17;
|
96
|
+
}
|
97
|
+
|
98
|
+
message RepeatedGroup {
|
99
|
+
int32 a = 47;
|
100
|
+
}
|
101
|
+
}
|
102
|
+
service TestService {
|
103
|
+
rpc TestMethod(TestAllTypes) returns (ForeignMessage) {
|
104
|
+
};
|
105
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
syntax = "proto3";
|
2
|
+
|
3
|
+
package referenced.v1;
|
4
|
+
|
5
|
+
import "simple/simple.proto";
|
6
|
+
|
7
|
+
message MessageA {
|
8
|
+
|
9
|
+
message MessageAA {
|
10
|
+
string name = 1;
|
11
|
+
simple.v1.SimpleMessage simple = 2;
|
12
|
+
}
|
13
|
+
|
14
|
+
message MessageAB {
|
15
|
+
string name = 1;
|
16
|
+
}
|
17
|
+
}
|
18
|
+
message MessageB {
|
19
|
+
|
20
|
+
message MessageBA {
|
21
|
+
string name = 1;
|
22
|
+
simple.v1.SimpleMessage simple = 2;
|
23
|
+
}
|
24
|
+
|
25
|
+
message MessageBB {
|
26
|
+
string name = 1;
|
27
|
+
}
|
28
|
+
}
|
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,13 @@
|
|
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.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Orner
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: google-protobuf
|
@@ -24,6 +23,20 @@ dependencies:
|
|
24
23
|
- - ">="
|
25
24
|
- !ruby/object:Gem::Version
|
26
25
|
version: '0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: excon
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
27
40
|
- !ruby/object:Gem::Dependency
|
28
41
|
name: bundler
|
29
42
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,27 +79,71 @@ dependencies:
|
|
66
79
|
- - "~>"
|
67
80
|
- !ruby/object:Gem::Version
|
68
81
|
version: '3.2'
|
69
|
-
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: webmock
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: standardrb
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
70
110
|
email:
|
71
111
|
- daniel.orner@flipp.com
|
72
112
|
executables: []
|
73
113
|
extensions: []
|
74
114
|
extra_rdoc_files: []
|
75
115
|
files:
|
116
|
+
- ".github/workflows/lint.yml"
|
117
|
+
- ".github/workflows/release.yml"
|
118
|
+
- ".github/workflows/test.yml"
|
119
|
+
- ".rspec"
|
76
120
|
- CHANGELOG.md
|
121
|
+
- Gemfile
|
122
|
+
- Gemfile.lock
|
77
123
|
- LICENSE
|
78
124
|
- README.md
|
125
|
+
- Rakefile
|
79
126
|
- lib/proto_turf.rb
|
80
127
|
- lib/proto_turf/cached_confluent_schema_registry.rb
|
81
128
|
- lib/proto_turf/confluent_schema_registry.rb
|
129
|
+
- lib/proto_turf/proto_text.rb
|
82
130
|
- lib/proto_turf/version.rb
|
83
131
|
- proto_turf.gemspec
|
132
|
+
- spec/decoding_spec.rb
|
133
|
+
- spec/encoding_spec.rb
|
134
|
+
- spec/gen/everything/everything_pb.rb
|
135
|
+
- spec/gen/referenced/referer_pb.rb
|
136
|
+
- spec/gen/simple/simple_pb.rb
|
137
|
+
- spec/proto_text_spec.rb
|
138
|
+
- spec/schemas/everything/everything.proto
|
139
|
+
- spec/schemas/referenced/referer.proto
|
140
|
+
- spec/schemas/simple/simple.proto
|
141
|
+
- spec/spec_helper.rb
|
84
142
|
homepage: https://github.com/flipp-oss/proto_turf
|
85
143
|
licenses:
|
86
144
|
- MIT
|
87
145
|
metadata:
|
88
146
|
rubygems_mfa_required: 'true'
|
89
|
-
post_install_message:
|
90
147
|
rdoc_options: []
|
91
148
|
require_paths:
|
92
149
|
- lib
|
@@ -101,8 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
101
158
|
- !ruby/object:Gem::Version
|
102
159
|
version: '0'
|
103
160
|
requirements: []
|
104
|
-
rubygems_version: 3.
|
105
|
-
signing_key:
|
161
|
+
rubygems_version: 3.6.9
|
106
162
|
specification_version: 4
|
107
163
|
summary: Support for Protobuf files in Confluent Schema Registry
|
108
164
|
test_files: []
|