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.
@@ -1,32 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'google/protobuf'
4
- require 'google/protobuf/well_known_types'
5
- require 'google/protobuf/descriptor_pb'
6
- require 'schema_registry_client/output/proto_text'
7
- require 'schema_registry_client/schema/base'
8
- require 'schema_registry_client/wire'
9
-
10
- class SchemaRegistry
3
+ require "google/protobuf"
4
+ require "google/protobuf/well_known_types"
5
+ require "google/protobuf/descriptor_pb"
6
+ require "schema_registry_client/output/proto_text"
7
+ require "schema_registry_client/schema/base"
8
+ require "schema_registry_client/wire"
9
+
10
+ module SchemaRegistry
11
11
  module Schema
12
12
  class Protobuf < Base
13
13
  class << self
14
14
  def schema_type
15
- 'PROTOBUF'
15
+ "PROTOBUF"
16
16
  end
17
17
 
18
18
  def schema_text(message, schema_name: nil)
19
19
  file_descriptor = if message.is_a?(Google::Protobuf::FileDescriptor)
20
- message
21
- else
22
- message.class.descriptor.file_descriptor
23
- end
20
+ message
21
+ else
22
+ message.class.descriptor.file_descriptor
23
+ end
24
24
  SchemaRegistry::Output::ProtoText.output(file_descriptor.to_proto)
25
25
  end
26
26
 
27
27
  def encode(message, stream, schema_name: nil)
28
28
  _, indexes = find_index(message.class.descriptor.to_proto,
29
- message.class.descriptor.file_descriptor.to_proto.message_type)
29
+ message.class.descriptor.file_descriptor.to_proto.message_type)
30
30
 
31
31
  if indexes == [0]
32
32
  SchemaRegistry::Wire.write_int(stream, 0)
@@ -60,7 +60,7 @@ class SchemaRegistry
60
60
  all_files = ObjectSpace.each_object(Google::Protobuf::FileDescriptor).to_a
61
61
  all_files.each do |file_desc|
62
62
  file_path = file_desc.name
63
- next if file_path.start_with?('google/protobuf/') # skip built-in protos
63
+ next if file_path.start_with?("google/protobuf/") # skip built-in protos
64
64
 
65
65
  @all_schemas[file_path] = file_desc
66
66
  end
@@ -69,13 +69,13 @@ class SchemaRegistry
69
69
  def dependencies(message)
70
70
  load_schemas! unless @all_schemas&.any?
71
71
  file_descriptor = if message.is_a?(Google::Protobuf::FileDescriptor)
72
- message
73
- else
74
- message.class.descriptor.file_descriptor
75
- end
72
+ message
73
+ else
74
+ message.class.descriptor.file_descriptor
75
+ end
76
76
 
77
77
  deps = file_descriptor.to_proto.dependency.to_a
78
- .reject { |d| d.start_with?('google/protobuf/') }
78
+ .reject { |d| d.start_with?("google/protobuf/") }
79
79
  deps.to_h do |dep|
80
80
  dependency_schema = @all_schemas[dep]
81
81
  [dependency_schema.name, dependency_schema]
@@ -117,12 +117,12 @@ class SchemaRegistry
117
117
  descriptor = Google::Protobuf::DescriptorPool.generated_pool.lookup(full_name)
118
118
  unless descriptor
119
119
  msg = "Could not find schema for #{full_name}. " \
120
- 'Make sure the corresponding .proto file has been compiled and loaded.'
120
+ "Make sure the corresponding .proto file has been compiled and loaded."
121
121
  raise msg
122
122
  end
123
123
 
124
124
  path = find_descriptor(indexes, descriptor.file_descriptor.to_proto.message_type)
125
- correct_message = Google::Protobuf::DescriptorPool.generated_pool.lookup("#{package}.#{path.join('.')}")
125
+ correct_message = Google::Protobuf::DescriptorPool.generated_pool.lookup("#{package}.#{path.join(".")}")
126
126
  correct_message.msgclass.decode(encoded)
127
127
  end
128
128
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class SchemaRegistry
4
- VERSION = '0.0.6'
3
+ module SchemaRegistry
4
+ VERSION = "0.0.7"
5
5
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class SchemaRegistry
3
+ module SchemaRegistry
4
4
  module Wire
5
5
  class << self
6
6
  # Write an int with zig-zag encoding. Copied from Avro.
@@ -1,156 +1,160 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'logger'
4
- require 'json'
5
- require 'schema_registry_client/confluent_schema_registry'
6
- require 'schema_registry_client/cached_confluent_schema_registry'
7
- require 'schema_registry_client/schema/protobuf'
8
- require 'schema_registry_client/schema/proto_json_schema'
9
- require 'schema_registry_client/schema/avro'
10
-
11
- class SchemaRegistry
3
+ require "logger"
4
+ require "json"
5
+ require "schema_registry_client/confluent_schema_registry"
6
+ require "schema_registry_client/cached_confluent_schema_registry"
7
+ require "schema_registry_client/schema/protobuf"
8
+ require "schema_registry_client/schema/proto_json_schema"
9
+ require "schema_registry_client/schema/avro"
10
+
11
+ module SchemaRegistry
12
12
  class SchemaNotFoundError < StandardError; end
13
13
  class SchemaError < StandardError; end
14
14
 
15
- # Provides a way to encode and decode messages without having to embed schemas
16
- # in the encoded data. Confluent's Schema Registry[1] is used to register
17
- # a schema when encoding a message -- the registry will issue a schema id that
18
- # will be included in the encoded data alongside the actual message. When
19
- # decoding the data, the schema id will be used to look up the writer's schema
20
- # from the registry.
21
- #
22
- # 1: https://github.com/confluentinc/schema-registry
23
- # https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/serdes-protobuf.html
24
- # https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format
25
- MAGIC_BYTE = [0].pack('C').freeze
26
-
27
- # Instantiate a new SchemaRegistry instance with the given configuration.
28
- #
29
- # registry - A schema registry object that responds to all methods in the
30
- # SchemaRegistry::ConfluentSchemaRegistry interface.
31
- # registry_url - The String URL of the schema registry that should be used.
32
- # schema_context - Schema registry context name (optional)
33
- # registry_path_prefix - The String URL path prefix used to namespace schema registry requests (optional).
34
- # logger - The Logger that should be used to log information (optional).
35
- # proxy - Forward the request via proxy (optional).
36
- # user - User for basic auth (optional).
37
- # password - Password for basic auth (optional).
38
- # ssl_ca_file - Name of file containing CA certificate (optional).
39
- # client_cert - Name of file containing client certificate (optional).
40
- # client_key - Name of file containing client private key to go with client_cert (optional).
41
- # client_key_pass - Password to go with client_key (optional).
42
- # client_cert_data - In-memory client certificate (optional).
43
- # client_key_data - In-memory client private key to go with client_cert_data (optional).
44
- # connect_timeout - Timeout to use in the connection with the schema registry (optional).
45
- # resolv_resolver - Custom domain name resolver (optional).
46
- # schema_type - A SchemaRegistry::Schema::Base subclass.
47
- def initialize( # rubocop:disable Metrics/ParameterLists
48
- registry: nil,
49
- registry_url: nil,
50
- schema_context: nil,
51
- registry_path_prefix: nil,
52
- logger: nil,
53
- proxy: nil,
54
- user: nil,
55
- password: nil,
56
- ssl_ca_file: nil,
57
- client_cert: nil,
58
- client_key: nil,
59
- client_key_pass: nil,
60
- client_cert_data: nil,
61
- client_key_data: nil,
62
- connect_timeout: nil,
63
- resolv_resolver: nil,
64
- schema_type: SchemaRegistry::Schema::Protobuf
65
- )
66
- @logger = logger || Logger.new($stderr)
67
- @registry = registry || SchemaRegistry::CachedConfluentSchemaRegistry.new(
68
- SchemaRegistry::ConfluentSchemaRegistry.new(
69
- registry_url,
70
- schema_context: schema_context,
71
- logger: @logger,
72
- proxy: proxy,
73
- user: user,
74
- password: password,
75
- ssl_ca_file: ssl_ca_file,
76
- client_cert: client_cert,
77
- client_key: client_key,
78
- client_key_pass: client_key_pass,
79
- client_cert_data: client_cert_data,
80
- client_key_data: client_key_data,
81
- path_prefix: registry_path_prefix,
82
- connect_timeout: connect_timeout,
83
- resolv_resolver: resolv_resolver
84
- )
85
- )
86
- @schema = schema_type
87
- end
88
-
89
15
  class << self
90
16
  attr_accessor :avro_schema_path
91
17
  end
92
18
 
93
- # Encodes a message using the specified schema.
94
- # @param message [Object] The message that should be encoded. Must be compatible with the schema.
95
- # @param subject [String] The subject name the schema should be registered under in the schema registry (optional).
96
- # @param schema_name [String] the name of the schema to use for encoding (optional).
97
- # @return [String] the encoded data.
98
- def encode(message, subject: nil, schema_text: nil, schema_name: nil)
99
- id = register_schema(message, subject, schema_text: schema_text, schema_name: schema_name)
100
-
101
- stream = StringIO.new
102
- # Always start with the magic byte.
103
- stream.write(MAGIC_BYTE)
19
+ class Client
20
+ # Provides a way to encode and decode messages without having to embed schemas
21
+ # in the encoded data. Confluent's Schema Registry[1] is used to register
22
+ # a schema when encoding a message -- the registry will issue a schema id that
23
+ # will be included in the encoded data alongside the actual message. When
24
+ # decoding the data, the schema id will be used to look up the writer's schema
25
+ # from the registry.
26
+ #
27
+ # 1: https://github.com/confluentinc/schema-registry
28
+ # https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/serdes-protobuf.html
29
+ # https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format
30
+ MAGIC_BYTE = [0].pack("C").freeze
31
+
32
+ # Instantiate a new SchemaRegistry instance with the given configuration.
33
+ #
34
+ # registry - A schema registry object that responds to all methods in the
35
+ # SchemaRegistry::ConfluentSchemaRegistry interface.
36
+ # registry_url - The String URL of the schema registry that should be used.
37
+ # schema_context - Schema registry context name (optional)
38
+ # registry_path_prefix - The String URL path prefix used to namespace schema registry requests (optional).
39
+ # logger - The Logger that should be used to log information (optional).
40
+ # proxy - Forward the request via proxy (optional).
41
+ # user - User for basic auth (optional).
42
+ # password - Password for basic auth (optional).
43
+ # ssl_ca_file - Name of file containing CA certificate (optional).
44
+ # client_cert - Name of file containing client certificate (optional).
45
+ # client_key - Name of file containing client private key to go with client_cert (optional).
46
+ # client_key_pass - Password to go with client_key (optional).
47
+ # client_cert_data - In-memory client certificate (optional).
48
+ # client_key_data - In-memory client private key to go with client_cert_data (optional).
49
+ # connect_timeout - Timeout to use in the connection with the schema registry (optional).
50
+ # resolv_resolver - Custom domain name resolver (optional).
51
+ # schema_type - A SchemaRegistry::Schema::Base subclass.
52
+ def initialize( # rubocop:disable Metrics/ParameterLists
53
+ registry: nil,
54
+ registry_url: nil,
55
+ schema_context: nil,
56
+ registry_path_prefix: nil,
57
+ logger: nil,
58
+ proxy: nil,
59
+ user: nil,
60
+ password: nil,
61
+ ssl_ca_file: nil,
62
+ client_cert: nil,
63
+ client_key: nil,
64
+ client_key_pass: nil,
65
+ client_cert_data: nil,
66
+ client_key_data: nil,
67
+ connect_timeout: nil,
68
+ resolv_resolver: nil,
69
+ schema_type: SchemaRegistry::Schema::Protobuf
70
+ )
71
+ @logger = logger || Logger.new($stderr)
72
+ @registry = registry || SchemaRegistry::CachedConfluentSchemaRegistry.new(
73
+ SchemaRegistry::ConfluentSchemaRegistry.new(
74
+ registry_url,
75
+ schema_context: schema_context,
76
+ logger: @logger,
77
+ proxy: proxy,
78
+ user: user,
79
+ password: password,
80
+ ssl_ca_file: ssl_ca_file,
81
+ client_cert: client_cert,
82
+ client_key: client_key,
83
+ client_key_pass: client_key_pass,
84
+ client_cert_data: client_cert_data,
85
+ client_key_data: client_key_data,
86
+ path_prefix: registry_path_prefix,
87
+ connect_timeout: connect_timeout,
88
+ resolv_resolver: resolv_resolver
89
+ )
90
+ )
91
+ @schema = schema_type
92
+ end
104
93
 
105
- # The schema id is encoded as a 4-byte big-endian integer.
106
- stream.write([id].pack('N'))
94
+ # Encodes a message using the specified schema.
95
+ # @param message [Object] The message that should be encoded. Must be compatible with the schema.
96
+ # @param subject [String] The subject name the schema should be registered under in the schema registry (optional).
97
+ # @param schema_name [String] the name of the schema to use for encoding (optional).
98
+ # @return [String] the encoded data.
99
+ def encode(message, subject: nil, schema_text: nil, schema_name: nil)
100
+ id = register_schema(message, subject, schema_text: schema_text, schema_name: schema_name)
107
101
 
108
- @schema.encode(message, stream, schema_name: schema_name)
109
- stream.string
110
- end
102
+ stream = StringIO.new
103
+ # Always start with the magic byte.
104
+ stream.write(MAGIC_BYTE)
111
105
 
112
- # Decodes data into the original message.
113
- #
114
- # @param data [String] a string containing encoded data.
115
- # @return [Object] the decoded data.
116
- def decode(data)
117
- stream = StringIO.new(data)
106
+ # The schema id is encoded as a 4-byte big-endian integer.
107
+ stream.write([id].pack("N"))
118
108
 
119
- # The first byte is MAGIC!!!
120
- magic_byte = stream.read(1)
109
+ @schema.encode(message, stream, schema_name: schema_name)
110
+ stream.string
111
+ end
121
112
 
122
- raise "Expected data to begin with a magic byte, got `#{magic_byte.inspect}`" if magic_byte != MAGIC_BYTE
113
+ # Decodes data into the original message.
114
+ #
115
+ # @param data [String] a string containing encoded data.
116
+ # @return [Object] the decoded data.
117
+ def decode(data)
118
+ stream = StringIO.new(data)
123
119
 
124
- # The schema id is a 4-byte big-endian integer.
125
- schema_id = stream.read(4).unpack1('N')
126
- schema = @registry.fetch(schema_id)
127
- @schema.decode(stream, schema)
128
- rescue Excon::Error::NotFound
129
- raise SchemaNotFoundError, "Schema with id: #{schema_id} is not found on registry"
130
- end
120
+ # The first byte is MAGIC!!!
121
+ magic_byte = stream.read(1)
131
122
 
132
- private
123
+ raise "Expected data to begin with a magic byte, got `#{magic_byte.inspect}`" if magic_byte != MAGIC_BYTE
133
124
 
134
- def register_schema(message, subject, schema_text: nil, schema_name: nil)
135
- schema_text ||= @schema.schema_text(message, schema_name: schema_name)
136
- return if @registry.registered?(schema_text, subject)
125
+ # The schema id is a 4-byte big-endian integer.
126
+ schema_id = stream.read(4).unpack1("N")
127
+ schema = @registry.fetch(schema_id)
128
+ @schema.decode(stream, schema)
129
+ rescue Excon::Error::NotFound
130
+ raise SchemaNotFoundError, "Schema with id: #{schema_id} is not found on registry"
131
+ end
137
132
 
138
- # register dependencies first
139
- dependencies = @schema.dependencies(message)
140
- versions = dependencies.map do |name, dependency|
141
- result = register_schema(dependency, name)
142
- @registry.fetch_version(result, name)
133
+ private
134
+
135
+ def register_schema(message, subject, schema_text: nil, schema_name: nil)
136
+ schema_text ||= @schema.schema_text(message, schema_name: schema_name)
137
+ return if @registry.registered?(schema_text, subject)
138
+
139
+ # register dependencies first
140
+ dependencies = @schema.dependencies(message)
141
+ versions = dependencies.map do |name, dependency|
142
+ result = register_schema(dependency, name)
143
+ @registry.fetch_version(result, name)
144
+ end
145
+
146
+ @registry.register(subject,
147
+ schema_text,
148
+ references: dependencies.keys.map.with_index do |dependency, i|
149
+ {
150
+ name: dependency,
151
+ subject: dependency,
152
+ version: versions[i]
153
+ }
154
+ end,
155
+ schema_type: @schema.schema_type)
143
156
  end
144
157
 
145
- @registry.register(subject,
146
- schema_text,
147
- references: dependencies.keys.map.with_index do |dependency, i|
148
- {
149
- name: dependency,
150
- subject: dependency,
151
- version: versions[i]
152
- }
153
- end,
154
- schema_type: @schema.schema_type)
155
158
  end
159
+
156
160
  end
@@ -1,33 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path('lib', __dir__)
3
+ lib = File.expand_path("lib", __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require 'schema_registry_client/version'
5
+ require "schema_registry_client/version"
6
6
 
7
7
  Gem::Specification.new do |spec|
8
- spec.name = 'schema_registry_client'
8
+ spec.name = "schema_registry_client"
9
9
  spec.version = SchemaRegistry::VERSION
10
- spec.authors = ['Daniel Orner']
11
- spec.email = ['daniel.orner@flipp.com']
12
- spec.summary = 'Confluent Schema Registry client with support for Avro and Protobuf'
13
- spec.homepage = 'https://github.com/flipp-oss/schema_registry_client'
14
- spec.license = 'MIT'
15
- spec.required_ruby_version = '>= 3.0'
10
+ spec.authors = ["Daniel Orner"]
11
+ spec.email = ["daniel.orner@flipp.com"]
12
+ spec.summary = "Confluent Schema Registry client with support for Avro and Protobuf"
13
+ spec.homepage = "https://github.com/flipp-oss/schema_registry_client"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 3.0"
16
16
 
17
- spec.metadata['rubygems_mfa_required'] = 'true'
17
+ spec.metadata["rubygems_mfa_required"] = "true"
18
18
 
19
19
  spec.files = `git ls-files -z`.split("\x0")
20
20
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
- spec.require_paths = ['lib']
21
+ spec.require_paths = ["lib"]
22
22
 
23
- spec.add_dependency 'avro'
24
- spec.add_dependency 'excon'
25
- spec.add_dependency 'google-protobuf'
23
+ spec.add_dependency "avro"
24
+ spec.add_dependency "excon"
25
+ spec.add_dependency "google-protobuf"
26
26
 
27
- spec.add_development_dependency 'bundler', '~> 2.0'
28
- spec.add_development_dependency 'rake', '~> 13.0'
29
- spec.add_development_dependency 'rspec', '~> 3.2'
30
- spec.add_development_dependency 'simplecov'
31
- spec.add_development_dependency 'standardrb'
32
- spec.add_development_dependency 'webmock'
27
+ spec.add_development_dependency "bundler", "~> 2.0"
28
+ spec.add_development_dependency "rake", "~> 13.0"
29
+ spec.add_development_dependency "rspec", "~> 3.2"
30
+ spec.add_development_dependency "simplecov"
31
+ spec.add_development_dependency "standardrb"
32
+ spec.add_development_dependency "webmock"
33
33
  end