schema_registry_client 0.0.6 → 0.0.7
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/CHANGELOG.md +5 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/Rakefile +1 -1
- data/lib/schema_registry_client/avro_schema_store.rb +10 -10
- data/lib/schema_registry_client/cached_confluent_schema_registry.rb +6 -6
- data/lib/schema_registry_client/confluent_schema_registry.rb +13 -13
- data/lib/schema_registry_client/output/json_schema.rb +13 -13
- data/lib/schema_registry_client/output/proto_text.rb +46 -46
- data/lib/schema_registry_client/schema/avro.rb +9 -9
- data/lib/schema_registry_client/schema/base.rb +5 -5
- data/lib/schema_registry_client/schema/proto_json_schema.rb +4 -4
- data/lib/schema_registry_client/schema/protobuf.rb +22 -22
- data/lib/schema_registry_client/version.rb +2 -2
- data/lib/schema_registry_client/wire.rb +1 -1
- data/lib/schema_registry_client.rb +138 -134
- data/schema_registry_client.gemspec +20 -20
- data/spec/decoding_spec.rb +54 -54
- data/spec/encoding_spec.rb +91 -91
- data/spec/gen/everything/everything_pb.rb +10 -10
- data/spec/gen/referenced/referer_pb.rb +8 -8
- data/spec/gen/simple/simple_pb.rb +3 -3
- data/spec/json_schema_spec.rb +2 -2
- data/spec/proto_text_spec.rb +1 -1
- data/spec/spec_helper.rb +5 -5
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 11e7f32b7a5774218f42c65cb0e23bf078409d3db664e077362cb956c408f612
|
|
4
|
+
data.tar.gz: a814e18b0ceb5a6794a9953f2c17793ea246e4d021d71cc7b543f784c7223070
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 47e021f857a27b93e9800ff93c376493e2c408eeba06ca1a0549b8909b4d0e19f5372667b34548171208e610a63f03d9960223bf95cd2c2508fc9f8d167df130
|
|
7
|
+
data.tar.gz: 55e451acce060a38616a53889973b2640dfde252e1455a1f324789440dda11c6b3860995f188fdea54daa71e4ecf7a7b024964fa665f9e411744418f9f14c457
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## UNRELEASED
|
|
9
9
|
|
|
10
|
+
# 0.0.7 - 2026-01-05
|
|
11
|
+
|
|
12
|
+
* Switch to using `SchemaRegistry::Client` instead of bare `SchemaRegistry`.
|
|
13
|
+
* Throw correct error when Avro schema is not found.
|
|
14
|
+
|
|
10
15
|
# 0.0.6 - 2026-01-02
|
|
11
16
|
|
|
12
17
|
* Initial release.
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -27,7 +27,7 @@ Example usage:
|
|
|
27
27
|
```ruby
|
|
28
28
|
require 'schema_registry_client'
|
|
29
29
|
|
|
30
|
-
schema_registry_client = SchemaRegistry.new(registry_url: 'http://localhost:8081', schema_paths: ['path/to/protos'])
|
|
30
|
+
schema_registry_client = SchemaRegistry::Client.new(registry_url: 'http://localhost:8081', schema_paths: ['path/to/protos'])
|
|
31
31
|
message = MyProto::MyMessage.new(field1: 'value1', field2: 42)
|
|
32
32
|
encoded = schema_registry_client.encode(message, subject: 'my-subject')
|
|
33
33
|
|
data/Rakefile
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "avro"
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
module SchemaRegistry
|
|
6
6
|
class AvroSchemaStore
|
|
7
7
|
def initialize(path: nil)
|
|
8
|
-
@path = path or raise
|
|
8
|
+
@path = path or raise "Please specify a schema path"
|
|
9
9
|
@schemas = {}
|
|
10
10
|
@schema_text = {}
|
|
11
11
|
@mutex = Mutex.new
|
|
@@ -38,14 +38,14 @@ class SchemaRegistry
|
|
|
38
38
|
|
|
39
39
|
# Loads all schema definition files in the `schemas_dir`.
|
|
40
40
|
def load_schemas!
|
|
41
|
-
pattern = [@path,
|
|
41
|
+
pattern = [@path, "**", "*.avsc"].join("/")
|
|
42
42
|
|
|
43
43
|
Dir.glob(pattern) do |schema_path|
|
|
44
44
|
# Remove the path prefix.
|
|
45
|
-
schema_path.sub!(%r{^/?#{@path}/},
|
|
45
|
+
schema_path.sub!(%r{^/?#{@path}/}, "")
|
|
46
46
|
|
|
47
47
|
# Replace `/` with `.` and chop off the file extension.
|
|
48
|
-
schema_name = File.basename(schema_path.tr(
|
|
48
|
+
schema_name = File.basename(schema_path.tr("/", "."), ".avsc")
|
|
49
49
|
|
|
50
50
|
# Load and cache the schema.
|
|
51
51
|
find(schema_name)
|
|
@@ -54,8 +54,8 @@ class SchemaRegistry
|
|
|
54
54
|
|
|
55
55
|
# @param schema_hash [Hash]
|
|
56
56
|
def add_schema(schema_hash)
|
|
57
|
-
name = schema_hash[
|
|
58
|
-
namespace = schema_hash[
|
|
57
|
+
name = schema_hash["name"]
|
|
58
|
+
namespace = schema_hash["namespace"]
|
|
59
59
|
full_name = Avro::Name.make_fullname(name, namespace)
|
|
60
60
|
return if @schemas.key?(full_name)
|
|
61
61
|
|
|
@@ -116,11 +116,11 @@ class SchemaRegistry
|
|
|
116
116
|
end
|
|
117
117
|
load_schema!(fullname, @schemas.dup)
|
|
118
118
|
rescue Errno::ENOENT, Errno::ENAMETOOLONG
|
|
119
|
-
raise "could not find Avro schema at `#{schema_path}'"
|
|
119
|
+
raise SchemaRegistry::SchemaNotFoundError, "could not find Avro schema at `#{schema_path}'"
|
|
120
120
|
end
|
|
121
121
|
|
|
122
122
|
def build_schema_path(fullname)
|
|
123
|
-
*namespace, schema_name = fullname.split(
|
|
123
|
+
*namespace, schema_name = fullname.split(".")
|
|
124
124
|
File.join(@path, *namespace, "#{schema_name}.avsc")
|
|
125
125
|
end
|
|
126
126
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
module SchemaRegistry
|
|
4
4
|
class CachedConfluentSchemaRegistry
|
|
5
5
|
# @param upstream [SchemaRegistry::ConfluentSchemaRegistry]
|
|
6
6
|
def initialize(upstream)
|
|
@@ -31,7 +31,7 @@ class SchemaRegistry
|
|
|
31
31
|
return @versions_by_subject_and_id[key] if @versions_by_subject_and_id[key]
|
|
32
32
|
|
|
33
33
|
results = @upstream.schema_subject_versions(id)
|
|
34
|
-
@versions_by_subject_and_id[key] = results&.find { |r| r[
|
|
34
|
+
@versions_by_subject_and_id[key] = results&.find { |r| r["subject"] == subject }&.dig("version")
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
# @param subject [String] the subject to check
|
|
@@ -45,13 +45,13 @@ class SchemaRegistry
|
|
|
45
45
|
# @param schema [String] the schema text to register
|
|
46
46
|
# @param references [Array<Hash>] optional references to other schemas
|
|
47
47
|
# @param schema_type [String]
|
|
48
|
-
def register(subject, schema, references: [], schema_type:
|
|
48
|
+
def register(subject, schema, references: [], schema_type: "PROTOBUF")
|
|
49
49
|
key = [subject, schema]
|
|
50
50
|
|
|
51
51
|
@ids_by_schema[key] ||= @upstream.register(subject,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
schema,
|
|
53
|
+
references: references,
|
|
54
|
+
schema_type: schema_type)
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
end
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "excon"
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
module SchemaRegistry
|
|
6
6
|
class ConfluentSchemaRegistry
|
|
7
|
-
CONTENT_TYPE =
|
|
7
|
+
CONTENT_TYPE = "application/vnd.schemaregistry.v1+json"
|
|
8
8
|
|
|
9
9
|
def initialize( # rubocop:disable Metrics/ParameterLists
|
|
10
10
|
url,
|
|
@@ -25,11 +25,11 @@ class SchemaRegistry
|
|
|
25
25
|
retry_limit: nil
|
|
26
26
|
)
|
|
27
27
|
@path_prefix = path_prefix
|
|
28
|
-
@schema_context_prefix = schema_context.nil? ?
|
|
29
|
-
@schema_context_options = schema_context.nil? ? {} : {
|
|
28
|
+
@schema_context_prefix = schema_context.nil? ? "" : ":.#{schema_context}:"
|
|
29
|
+
@schema_context_options = schema_context.nil? ? {} : {query: {subject: @schema_context_prefix}}
|
|
30
30
|
@logger = logger
|
|
31
31
|
headers = Excon.defaults[:headers].merge(
|
|
32
|
-
|
|
32
|
+
"Content-Type" => CONTENT_TYPE
|
|
33
33
|
)
|
|
34
34
|
params = {
|
|
35
35
|
headers: headers,
|
|
@@ -59,7 +59,7 @@ class SchemaRegistry
|
|
|
59
59
|
def fetch(id)
|
|
60
60
|
@logger.info "Fetching schema with id #{id}"
|
|
61
61
|
data = get("/schemas/ids/#{id}", idempotent: true, **@schema_context_options)
|
|
62
|
-
data.fetch(
|
|
62
|
+
data.fetch("schema")
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
# @param schema_id [Integer] the schema ID to fetch versions for
|
|
@@ -72,13 +72,13 @@ class SchemaRegistry
|
|
|
72
72
|
# @param schema [String] the schema text to check
|
|
73
73
|
# @param references [Array<Hash>] optional references to other schemas
|
|
74
74
|
# @return [Integer] the ID of the registered schema
|
|
75
|
-
def register(subject, schema, references: [], schema_type:
|
|
75
|
+
def register(subject, schema, references: [], schema_type: "PROTOBUF")
|
|
76
76
|
data = post("/subjects/#{@schema_context_prefix}#{CGI.escapeURIComponent(subject)}/versions",
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
body: {schemaType: schema_type,
|
|
78
|
+
references: references,
|
|
79
|
+
schema: schema.to_s}.to_json)
|
|
80
80
|
|
|
81
|
-
id = data.fetch(
|
|
81
|
+
id = data.fetch("id")
|
|
82
82
|
|
|
83
83
|
@logger.info "Registered schema for subject `#{@schema_context_prefix}#{subject}`; id = #{id}"
|
|
84
84
|
|
|
@@ -106,7 +106,7 @@ class SchemaRegistry
|
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
def request(path, **options)
|
|
109
|
-
options = {
|
|
109
|
+
options = {expects: 200}.merge!(options)
|
|
110
110
|
path = File.join(@path_prefix, path) unless @path_prefix.nil?
|
|
111
111
|
response = @connection.request(path: path, **options)
|
|
112
112
|
JSON.parse(response.body)
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
module SchemaRegistry
|
|
4
4
|
module Output
|
|
5
5
|
module JsonSchema
|
|
6
6
|
class << self
|
|
7
7
|
def fetch(message_name)
|
|
8
|
-
name = message_name.start_with?(
|
|
8
|
+
name = message_name.start_with?(".") ? message_name[1..] : message_name
|
|
9
9
|
Google::Protobuf::DescriptorPool.generated_pool.lookup(name)
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def output(descriptor, path: nil)
|
|
13
13
|
properties = {}
|
|
14
14
|
result = {
|
|
15
|
-
|
|
16
|
-
type:
|
|
15
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
16
|
+
type: "object",
|
|
17
17
|
properties: properties
|
|
18
18
|
}
|
|
19
19
|
if path
|
|
20
20
|
# follow path down
|
|
21
|
-
parts = path.split(
|
|
21
|
+
parts = path.split(".")
|
|
22
22
|
field_name = parts.last
|
|
23
23
|
parts[...-1].each do |part|
|
|
24
24
|
field = descriptor.field.find { |f| f.name == part }
|
|
@@ -42,12 +42,12 @@ class SchemaRegistry
|
|
|
42
42
|
if field.label == :LABEL_REPEATED && !ignore_repeated
|
|
43
43
|
if klass&.options.respond_to?(:map_entry) && klass.options.map_entry
|
|
44
44
|
return {
|
|
45
|
-
type:
|
|
45
|
+
type: "object",
|
|
46
46
|
additionalProperties: field_object(klass.field[1])
|
|
47
47
|
}
|
|
48
48
|
end
|
|
49
49
|
return {
|
|
50
|
-
type:
|
|
50
|
+
type: "array",
|
|
51
51
|
items: field_object(field, ignore_repeated: true)
|
|
52
52
|
}
|
|
53
53
|
end
|
|
@@ -57,18 +57,18 @@ class SchemaRegistry
|
|
|
57
57
|
def field_type(field, klass)
|
|
58
58
|
case field.type
|
|
59
59
|
when :TYPE_INT32, :TYPE_UINT32, :TYPE_SINT32, :TYPE_FIXED32, :TYPE_SFIXED32
|
|
60
|
-
{
|
|
60
|
+
{type: "integer"}
|
|
61
61
|
when :TYPE_FLOAT, :TYPE_DOUBLE
|
|
62
|
-
{
|
|
62
|
+
{type: "number"}
|
|
63
63
|
when :TYPE_INT64, :TYPE_UINT64, :TYPE_SINT64, :TYPE_FIXED64, :TYPE_SFIXED64, :TYPE_STRING, :TYPE_BYTES
|
|
64
|
-
{
|
|
64
|
+
{type: "string"}
|
|
65
65
|
when :TYPE_BOOL
|
|
66
|
-
{
|
|
66
|
+
{type: "boolean"}
|
|
67
67
|
else
|
|
68
68
|
if klass.is_a?(Google::Protobuf::EnumDescriptorProto)
|
|
69
|
-
{
|
|
69
|
+
{enum: klass.to_h[:value].map { |h| h[:name] }}
|
|
70
70
|
else
|
|
71
|
-
{
|
|
71
|
+
{type: "object"}
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
74
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
module SchemaRegistry
|
|
4
4
|
module Output
|
|
5
5
|
module ProtoText
|
|
6
6
|
ParseInfo = Struct.new(:writer, :package, :message) do
|
|
@@ -18,7 +18,7 @@ class SchemaRegistry
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def write_indent(str)
|
|
21
|
-
@indent.times { write(
|
|
21
|
+
@indent.times { write(" ") }
|
|
22
22
|
write(str)
|
|
23
23
|
end
|
|
24
24
|
|
|
@@ -42,7 +42,7 @@ class SchemaRegistry
|
|
|
42
42
|
|
|
43
43
|
class << self
|
|
44
44
|
def fetch(message_name)
|
|
45
|
-
name = message_name.start_with?(
|
|
45
|
+
name = message_name.start_with?(".") ? message_name[1..] : message_name
|
|
46
46
|
Google::Protobuf::DescriptorPool.generated_pool.lookup(name)
|
|
47
47
|
end
|
|
48
48
|
|
|
@@ -87,23 +87,23 @@ class SchemaRegistry
|
|
|
87
87
|
info.indent
|
|
88
88
|
write_field(info, extension)
|
|
89
89
|
info.dedent
|
|
90
|
-
info.write_line(
|
|
90
|
+
info.write_line("}")
|
|
91
91
|
end
|
|
92
92
|
descriptor.extension.any?
|
|
93
93
|
end
|
|
94
94
|
|
|
95
95
|
def write_reserved(writer, descriptor)
|
|
96
96
|
reserved = descriptor.reserved_range.map do |range|
|
|
97
|
-
range.start == range.end - 1 ? range.start.to_s : "#{range.start} to #{range.end - 1}"
|
|
97
|
+
(range.start == range.end - 1) ? range.start.to_s : "#{range.start} to #{range.end - 1}"
|
|
98
98
|
end
|
|
99
99
|
found = false
|
|
100
100
|
if reserved.any?
|
|
101
101
|
found = true
|
|
102
|
-
writer.write_line("reserved #{reserved.join(
|
|
102
|
+
writer.write_line("reserved #{reserved.join(", ")};")
|
|
103
103
|
end
|
|
104
104
|
if descriptor.reserved_name.any?
|
|
105
105
|
found = true
|
|
106
|
-
writer.write_line("reserved #{descriptor.reserved_name.map(&:to_json).join(
|
|
106
|
+
writer.write_line("reserved #{descriptor.reserved_name.map(&:to_json).join(", ")};")
|
|
107
107
|
end
|
|
108
108
|
writer.writenl if found
|
|
109
109
|
end
|
|
@@ -124,7 +124,7 @@ class SchemaRegistry
|
|
|
124
124
|
|
|
125
125
|
def write_message(info, message_type)
|
|
126
126
|
info.message = message_type
|
|
127
|
-
info.write_indent(
|
|
127
|
+
info.write_indent("message ")
|
|
128
128
|
info.write("#{message_type.name} {")
|
|
129
129
|
info.writenl
|
|
130
130
|
info.indent
|
|
@@ -150,61 +150,61 @@ class SchemaRegistry
|
|
|
150
150
|
write_message(info, subtype)
|
|
151
151
|
end
|
|
152
152
|
info.dedent
|
|
153
|
-
info.write_line(
|
|
153
|
+
info.write_line("}")
|
|
154
154
|
end
|
|
155
155
|
|
|
156
156
|
def field_type(info, field_type)
|
|
157
157
|
case field_type.type
|
|
158
158
|
when :TYPE_INT32
|
|
159
|
-
|
|
159
|
+
"int32"
|
|
160
160
|
when :TYPE_INT64
|
|
161
|
-
|
|
161
|
+
"int64"
|
|
162
162
|
when :TYPE_UINT32
|
|
163
|
-
|
|
163
|
+
"uint32"
|
|
164
164
|
when :TYPE_UINT64
|
|
165
|
-
|
|
165
|
+
"uint64"
|
|
166
166
|
when :TYPE_SINT32
|
|
167
|
-
|
|
167
|
+
"sint32"
|
|
168
168
|
when :TYPE_SINT64
|
|
169
|
-
|
|
169
|
+
"sint64"
|
|
170
170
|
when :TYPE_FIXED32
|
|
171
|
-
|
|
171
|
+
"fixed32"
|
|
172
172
|
when :TYPE_FIXED64
|
|
173
|
-
|
|
173
|
+
"fixed64"
|
|
174
174
|
when :TYPE_SFIXED32
|
|
175
|
-
|
|
175
|
+
"sfixed32"
|
|
176
176
|
when :TYPE_SFIXED64
|
|
177
|
-
|
|
177
|
+
"sfixed64"
|
|
178
178
|
when :TYPE_FLOAT
|
|
179
|
-
|
|
179
|
+
"float"
|
|
180
180
|
when :TYPE_DOUBLE
|
|
181
|
-
|
|
181
|
+
"double"
|
|
182
182
|
when :TYPE_BOOL
|
|
183
|
-
|
|
183
|
+
"bool"
|
|
184
184
|
when :TYPE_STRING
|
|
185
|
-
|
|
185
|
+
"string"
|
|
186
186
|
when :TYPE_BYTES
|
|
187
|
-
|
|
187
|
+
"bytes"
|
|
188
188
|
when :TYPE_ENUM, :TYPE_MESSAGE
|
|
189
189
|
# remove leading .
|
|
190
190
|
type = fetch(field_type.type_name[1..])
|
|
191
|
-
name = type.name.sub("#{info.package}.#{info.message.name}.",
|
|
192
|
-
name.sub("#{info.package}.",
|
|
191
|
+
name = type.name.sub("#{info.package}.#{info.message.name}.", "")
|
|
192
|
+
name.sub("#{info.package}.", "")
|
|
193
193
|
end
|
|
194
194
|
end
|
|
195
195
|
|
|
196
196
|
def write_field(info, field, oneof: false)
|
|
197
197
|
return if !oneof && field.has_oneof_index?
|
|
198
198
|
|
|
199
|
-
info.write_indent(
|
|
199
|
+
info.write_indent("")
|
|
200
200
|
|
|
201
201
|
klass = nil
|
|
202
|
-
klass = fetch(field.type_name).to_proto if field.type_name && field.type_name !=
|
|
202
|
+
klass = fetch(field.type_name).to_proto if field.type_name && field.type_name != ""
|
|
203
203
|
|
|
204
204
|
if field.proto3_optional
|
|
205
|
-
info.write(
|
|
205
|
+
info.write("optional ")
|
|
206
206
|
elsif field.label == :LABEL_REPEATED && !klass&.options&.map_entry
|
|
207
|
-
info.write(
|
|
207
|
+
info.write("repeated ")
|
|
208
208
|
end
|
|
209
209
|
|
|
210
210
|
if klass&.options&.map_entry
|
|
@@ -215,24 +215,24 @@ class SchemaRegistry
|
|
|
215
215
|
info.write(" #{field.name} = #{field.number}")
|
|
216
216
|
|
|
217
217
|
write_field_options(info, field)
|
|
218
|
-
info.write(
|
|
218
|
+
info.write(";")
|
|
219
219
|
info.writenl
|
|
220
220
|
end
|
|
221
221
|
|
|
222
222
|
def write_field_options(info, field)
|
|
223
223
|
return unless field.options
|
|
224
224
|
|
|
225
|
-
info.write(
|
|
226
|
-
info.write(field.options.to_h.map { |name, value| "#{name} = #{value}" }.join(
|
|
225
|
+
info.write(" [")
|
|
226
|
+
info.write(field.options.to_h.map { |name, value| "#{name} = #{value}" }.join(", "))
|
|
227
227
|
write_options(info, field, include_option_label: false)
|
|
228
|
-
info.write(
|
|
228
|
+
info.write("]")
|
|
229
229
|
end
|
|
230
230
|
|
|
231
231
|
def write_oneofs(info, message)
|
|
232
232
|
message.oneof_decl.each_with_index do |oneof, i|
|
|
233
233
|
# synthetic oneof for proto3 optional fields
|
|
234
|
-
next if oneof.name.start_with?(
|
|
235
|
-
|
|
234
|
+
next if oneof.name.start_with?("_") &&
|
|
235
|
+
message.field.any? { |f| f.proto3_optional && f.name == oneof.name[1..] }
|
|
236
236
|
|
|
237
237
|
info.write_line("oneof #{oneof.name} {")
|
|
238
238
|
info.indent
|
|
@@ -240,12 +240,12 @@ class SchemaRegistry
|
|
|
240
240
|
write_field(info, field, oneof: true)
|
|
241
241
|
end
|
|
242
242
|
info.dedent
|
|
243
|
-
info.write_line(
|
|
243
|
+
info.write_line("}")
|
|
244
244
|
end
|
|
245
245
|
end
|
|
246
246
|
|
|
247
247
|
def write_enum(info, enum_type)
|
|
248
|
-
info.write(
|
|
248
|
+
info.write("enum ")
|
|
249
249
|
info.write("#{enum_type.name} {")
|
|
250
250
|
info.writenl
|
|
251
251
|
info.indent
|
|
@@ -254,30 +254,30 @@ class SchemaRegistry
|
|
|
254
254
|
info.write_line("#{value.name} = #{value.number};")
|
|
255
255
|
end
|
|
256
256
|
info.dedent
|
|
257
|
-
info.write_line(
|
|
257
|
+
info.write_line("}")
|
|
258
258
|
info.writenl
|
|
259
259
|
end
|
|
260
260
|
|
|
261
261
|
def method_type(package, name)
|
|
262
|
-
output = name.sub("#{package}.",
|
|
263
|
-
output = output[1..] if output.start_with?(
|
|
262
|
+
output = name.sub("#{package}.", "")
|
|
263
|
+
output = output[1..] if output.start_with?(".")
|
|
264
264
|
output
|
|
265
265
|
end
|
|
266
266
|
|
|
267
267
|
def write_service(info, service)
|
|
268
268
|
info.write_line("service #{service.name} {")
|
|
269
269
|
info.indent
|
|
270
|
-
service[
|
|
270
|
+
service["method"].each do |method|
|
|
271
271
|
info.write_indent("rpc #{method.name}(#{method_type(info.package, method.input_type)}) ")
|
|
272
272
|
info.write("returns (#{method_type(info.package, method.output_type)}) {")
|
|
273
273
|
info.writenl
|
|
274
274
|
info.indent
|
|
275
275
|
write_options(info, method) if method.options
|
|
276
276
|
info.dedent
|
|
277
|
-
info.write_line(
|
|
277
|
+
info.write_line("};")
|
|
278
278
|
end
|
|
279
279
|
info.dedent
|
|
280
|
-
info.write_line(
|
|
280
|
+
info.write_line("}")
|
|
281
281
|
end
|
|
282
282
|
|
|
283
283
|
# @return [Boolean] true if any options were written
|
|
@@ -289,7 +289,7 @@ class SchemaRegistry
|
|
|
289
289
|
|
|
290
290
|
found = false
|
|
291
291
|
json.each_key do |name|
|
|
292
|
-
option_name = name.tr(
|
|
292
|
+
option_name = name.tr("[]", "")
|
|
293
293
|
ext = fetch(option_name)
|
|
294
294
|
next if ext.nil?
|
|
295
295
|
|
|
@@ -306,7 +306,7 @@ class SchemaRegistry
|
|
|
306
306
|
info.write_indent(line)
|
|
307
307
|
info.writenl if i < lines.length - 1
|
|
308
308
|
end
|
|
309
|
-
info.write(
|
|
309
|
+
info.write(";")
|
|
310
310
|
else
|
|
311
311
|
info.write(options.to_json)
|
|
312
312
|
end
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require "schema_registry_client/schema/base"
|
|
4
|
+
require "schema_registry_client/avro_schema_store"
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
module SchemaRegistry
|
|
7
7
|
module Schema
|
|
8
8
|
class Avro < Base
|
|
9
|
-
DEFAULT_SCHEMAS_PATH =
|
|
9
|
+
DEFAULT_SCHEMAS_PATH = "./schemas"
|
|
10
10
|
|
|
11
11
|
class << self
|
|
12
12
|
def schema_type
|
|
13
|
-
|
|
13
|
+
"AVRO"
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def schema_store
|
|
@@ -26,9 +26,9 @@ class SchemaRegistry
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def encode(message, stream, schema_name: nil)
|
|
29
|
-
validate_options = {
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
validate_options = {recursive: true,
|
|
30
|
+
encoded: false,
|
|
31
|
+
fail_on_extra_fields: true}
|
|
32
32
|
schema = schema_store.find(schema_name)
|
|
33
33
|
|
|
34
34
|
::Avro::SchemaValidator.validate!(schema, message, **validate_options)
|
|
@@ -48,7 +48,7 @@ class SchemaRegistry
|
|
|
48
48
|
# Try to find the reader schema locally, fall back to writer schema
|
|
49
49
|
readers_schema = begin
|
|
50
50
|
schema_store.find(writers_schema.fullname)
|
|
51
|
-
rescue
|
|
51
|
+
rescue
|
|
52
52
|
writers_schema
|
|
53
53
|
end
|
|
54
54
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
module SchemaRegistry
|
|
4
4
|
module Schema
|
|
5
5
|
class MissingImplementationError < StandardError; end
|
|
6
6
|
|
|
@@ -10,19 +10,19 @@ class SchemaRegistry
|
|
|
10
10
|
# @param schema_name [String]
|
|
11
11
|
# @return [String]
|
|
12
12
|
def schema_text(_message, schema_name: nil)
|
|
13
|
-
raise MissingImplementationError,
|
|
13
|
+
raise MissingImplementationError, "Subclasses must implement schema_text"
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
# @return [String]
|
|
17
17
|
def schema_type
|
|
18
|
-
raise MissingImplementationError,
|
|
18
|
+
raise MissingImplementationError, "Subclasses must implement schema_type"
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
# @param message [Object]
|
|
22
22
|
# @param stream [StringIO]
|
|
23
23
|
# @param schema_name [String]
|
|
24
24
|
def encode(_message, _stream, schema_name: nil)
|
|
25
|
-
raise MissingImplementationError,
|
|
25
|
+
raise MissingImplementationError, "Subclasses must implement encode"
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
# @param stream [StringIO]
|
|
@@ -30,7 +30,7 @@ class SchemaRegistry
|
|
|
30
30
|
# @param registry [Object]
|
|
31
31
|
# @return [Object]
|
|
32
32
|
def decode(_stream, _schema_text)
|
|
33
|
-
raise MissingImplementationError,
|
|
33
|
+
raise MissingImplementationError, "Subclasses must implement decode"
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
# @param message [Object]
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require "schema_registry_client/schema/base"
|
|
4
|
+
require "schema_registry_client/output/json_schema"
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
module SchemaRegistry
|
|
7
7
|
module Schema
|
|
8
8
|
class ProtoJsonSchema < Base
|
|
9
9
|
class << self
|
|
10
10
|
def schema_type
|
|
11
|
-
|
|
11
|
+
"JSON"
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def schema_text(message, schema_name: nil)
|