schema_registry_client 0.0.8 → 0.0.9

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8c4fb0ad39418dd0eb14e803f4c0652e47e64950b8684f564221a95b024177c1
4
- data.tar.gz: c04a0a44298ff123a0a63cf530fe2c2ae56b7230cc740e2d02577f712d045e41
3
+ metadata.gz: d0919f12f0457fbdf7becd62389a7ed72e34afe9d7f3ce5c819f2e0ebf145174
4
+ data.tar.gz: 4dc5c91669e508e89bc07763bebb6b754403ee72ccbe125ecfc888c48cf974fa
5
5
  SHA512:
6
- metadata.gz: c68cf8a284acdaad830acbbc76ea49b2cb1431b69e3c2961218f5c8af399b89d64f8b5eb1e25536e415f5720080152853f885d9984b47790ffd02d01cad73709
7
- data.tar.gz: 7fb3f61b7159bf9f461f3fc9b6f8d0f145592f18a35b1e02d284a6f2ed62c95d30a6d8ff1082b2607976f134e6a74e1416e00483e61f8db2ecf27f601112bd84
6
+ metadata.gz: 07d614eaceb30420a69221399fab2e360f3f3b4089e5271b42fcaca30eb9f643cebcdd59187bdfc6a33d721641b960e2855fefce4703e59f455e459088bbd996
7
+ data.tar.gz: 65a8cf4a6899e8f32febb2b408ebb20ed379224b7fbd0d716da33c6e0e6c10844559e5d4e1af73c3ee06a1601ecd0dc3853137aa3987837157bbd07c07655a21
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.9 - 2026-02-11
11
+
12
+ * Fix: Do not send `schemaType` if schema type is `AVRO`, for backwards compatibility with older schema registries.
13
+
10
14
  # 0.0.8 - 2026-02-04
11
15
 
12
16
  * Support passing a `schema_store` into the `Avro` schema backend.
data/Gemfile CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- source 'https://rubygems.org'
3
+ source "https://rubygems.org"
4
4
 
5
5
  gemspec
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- schema_registry_client (0.0.8)
4
+ schema_registry_client (0.0.9)
5
5
  avro
6
6
  excon
7
7
  google-protobuf
data/Rakefile CHANGED
@@ -1,3 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bundler/gem_tasks'
3
+ require "bundler/gem_tasks"
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'avro'
3
+ require "avro"
4
4
 
5
5
  module SchemaRegistry
6
6
  class AvroSchemaStore
7
7
  def initialize(path: nil)
8
- @path = path or raise 'Please specify a schema path'
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 @@ module SchemaRegistry
38
38
 
39
39
  # Loads all schema definition files in the `schemas_dir`.
40
40
  def load_schemas!
41
- pattern = [@path, '**', '*.avsc'].join('/')
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('/', '.'), '.avsc')
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 @@ module SchemaRegistry
54
54
 
55
55
  # @param schema_hash [Hash]
56
56
  def add_schema(schema_hash)
57
- name = schema_hash['name']
58
- namespace = schema_hash['namespace']
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
 
@@ -120,7 +120,7 @@ module SchemaRegistry
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
@@ -31,7 +31,7 @@ module 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['subject'] == subject }&.dig('version')
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 @@ module 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: 'PROTOBUF')
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
- schema,
53
- references: references,
54
- schema_type: schema_type)
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 'excon'
3
+ require "excon"
4
4
 
5
5
  module SchemaRegistry
6
6
  class ConfluentSchemaRegistry
7
- CONTENT_TYPE = 'application/vnd.schemaregistry.v1+json'
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 @@ module SchemaRegistry
25
25
  retry_limit: nil
26
26
  )
27
27
  @path_prefix = path_prefix
28
- @schema_context_prefix = schema_context.nil? ? '' : ":.#{schema_context}:"
29
- @schema_context_options = schema_context.nil? ? {} : { query: { subject: @schema_context_prefix } }
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
- 'Content-Type' => CONTENT_TYPE
32
+ "Content-Type" => CONTENT_TYPE
33
33
  )
34
34
  params = {
35
35
  headers: headers,
@@ -59,7 +59,7 @@ module 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('schema')
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,17 @@ module 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: 'PROTOBUF')
75
+ def register(subject, schema, references: [], schema_type: "PROTOBUF")
76
+ body = {references: references,
77
+ schema: schema.to_s}
78
+ # Not all schema registry versions support schemaType
79
+ if schema_type != "AVRO"
80
+ body[:schemaType] = schema_type
81
+ end
76
82
  data = post("/subjects/#{@schema_context_prefix}#{CGI.escapeURIComponent(subject)}/versions",
77
- body: { schemaType: schema_type,
78
- references: references,
79
- schema: schema.to_s }.to_json)
83
+ body: body.to_json)
80
84
 
81
- id = data.fetch('id')
85
+ id = data.fetch("id")
82
86
 
83
87
  @logger.info "Registered schema for subject `#{@schema_context_prefix}#{subject}`; id = #{id}"
84
88
 
@@ -106,7 +110,7 @@ module SchemaRegistry
106
110
  end
107
111
 
108
112
  def request(path, **options)
109
- options = { expects: 200 }.merge!(options)
113
+ options = {expects: 200}.merge!(options)
110
114
  path = File.join(@path_prefix, path) unless @path_prefix.nil?
111
115
  response = @connection.request(path: path, **options)
112
116
  JSON.parse(response.body)
@@ -5,20 +5,20 @@ module SchemaRegistry
5
5
  module JsonSchema
6
6
  class << self
7
7
  def fetch(message_name)
8
- name = message_name.start_with?('.') ? message_name[1..] : message_name
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
- "$schema": 'https://json-schema.org/draft/2020-12/schema',
16
- type: 'object',
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 @@ module 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: 'object',
45
+ type: "object",
46
46
  additionalProperties: field_object(klass.field[1])
47
47
  }
48
48
  end
49
49
  return {
50
- type: 'array',
50
+ type: "array",
51
51
  items: field_object(field, ignore_repeated: true)
52
52
  }
53
53
  end
@@ -57,18 +57,18 @@ module 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
- { type: 'integer' }
60
+ {type: "integer"}
61
61
  when :TYPE_FLOAT, :TYPE_DOUBLE
62
- { type: 'number' }
62
+ {type: "number"}
63
63
  when :TYPE_INT64, :TYPE_UINT64, :TYPE_SINT64, :TYPE_FIXED64, :TYPE_SFIXED64, :TYPE_STRING, :TYPE_BYTES
64
- { type: 'string' }
64
+ {type: "string"}
65
65
  when :TYPE_BOOL
66
- { type: 'boolean' }
66
+ {type: "boolean"}
67
67
  else
68
68
  if klass.is_a?(Google::Protobuf::EnumDescriptorProto)
69
- { enum: klass.to_h[:value].map { |h| h[:name] } }
69
+ {enum: klass.to_h[:value].map { |h| h[:name] }}
70
70
  else
71
- { type: 'object' }
71
+ {type: "object"}
72
72
  end
73
73
  end
74
74
  end
@@ -18,7 +18,7 @@ module 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 @@ module SchemaRegistry
42
42
 
43
43
  class << self
44
44
  def fetch(message_name)
45
- name = message_name.start_with?('.') ? message_name[1..] : message_name
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 @@ module 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 @@ module SchemaRegistry
124
124
 
125
125
  def write_message(info, message_type)
126
126
  info.message = message_type
127
- info.write_indent('message ')
127
+ info.write_indent("message ")
128
128
  info.write("#{message_type.name} {")
129
129
  info.writenl
130
130
  info.indent
@@ -150,61 +150,61 @@ module 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
- 'int32'
159
+ "int32"
160
160
  when :TYPE_INT64
161
- 'int64'
161
+ "int64"
162
162
  when :TYPE_UINT32
163
- 'uint32'
163
+ "uint32"
164
164
  when :TYPE_UINT64
165
- 'uint64'
165
+ "uint64"
166
166
  when :TYPE_SINT32
167
- 'sint32'
167
+ "sint32"
168
168
  when :TYPE_SINT64
169
- 'sint64'
169
+ "sint64"
170
170
  when :TYPE_FIXED32
171
- 'fixed32'
171
+ "fixed32"
172
172
  when :TYPE_FIXED64
173
- 'fixed64'
173
+ "fixed64"
174
174
  when :TYPE_SFIXED32
175
- 'sfixed32'
175
+ "sfixed32"
176
176
  when :TYPE_SFIXED64
177
- 'sfixed64'
177
+ "sfixed64"
178
178
  when :TYPE_FLOAT
179
- 'float'
179
+ "float"
180
180
  when :TYPE_DOUBLE
181
- 'double'
181
+ "double"
182
182
  when :TYPE_BOOL
183
- 'bool'
183
+ "bool"
184
184
  when :TYPE_STRING
185
- 'string'
185
+ "string"
186
186
  when :TYPE_BYTES
187
- 'bytes'
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('optional ')
205
+ info.write("optional ")
206
206
  elsif field.label == :LABEL_REPEATED && !klass&.options&.map_entry
207
- info.write('repeated ')
207
+ info.write("repeated ")
208
208
  end
209
209
 
210
210
  if klass&.options&.map_entry
@@ -215,24 +215,24 @@ module 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
- message.field.any? { |f| f.proto3_optional && f.name == oneof.name[1..] }
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 @@ module 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('enum ')
248
+ info.write("enum ")
249
249
  info.write("#{enum_type.name} {")
250
250
  info.writenl
251
251
  info.indent
@@ -254,30 +254,30 @@ module 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['method'].each do |method|
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 @@ module 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 @@ module 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,15 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'schema_registry_client/schema/base'
4
- require 'schema_registry_client/avro_schema_store'
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 = './schemas'
9
+ DEFAULT_SCHEMAS_PATH = "./schemas"
10
10
 
11
11
  def self.schema_type
12
- 'AVRO'
12
+ "AVRO"
13
13
  end
14
14
 
15
15
  # @param schema_store [SchemaRegistry::AvroSchemaStore, nil]
@@ -31,9 +31,9 @@ module SchemaRegistry
31
31
  end
32
32
 
33
33
  def encode(message, stream, schema_name: nil)
34
- validate_options = { recursive: true,
35
- encoded: false,
36
- fail_on_extra_fields: true }
34
+ validate_options = {recursive: true,
35
+ encoded: false,
36
+ fail_on_extra_fields: true}
37
37
  schema = schema_store.find(schema_name)
38
38
 
39
39
  ::Avro::SchemaValidator.validate!(schema, message, **validate_options)
@@ -53,7 +53,7 @@ module SchemaRegistry
53
53
  # Try to find the reader schema locally, fall back to writer schema
54
54
  readers_schema = begin
55
55
  schema_store.find(writers_schema.fullname)
56
- rescue StandardError
56
+ rescue
57
57
  writers_schema
58
58
  end
59
59
 
@@ -9,19 +9,19 @@ module SchemaRegistry
9
9
  # @param schema_name [String]
10
10
  # @return [String]
11
11
  def schema_text(_message, schema_name: nil)
12
- raise MissingImplementationError, 'Subclasses must implement schema_text'
12
+ raise MissingImplementationError, "Subclasses must implement schema_text"
13
13
  end
14
14
 
15
15
  # @return [String]
16
16
  def self.schema_type
17
- raise MissingImplementationError, 'Subclasses must implement schema_type'
17
+ raise MissingImplementationError, "Subclasses must implement schema_type"
18
18
  end
19
19
 
20
20
  # @param message [Object]
21
21
  # @param stream [StringIO]
22
22
  # @param schema_name [String]
23
23
  def encode(_message, _stream, schema_name: nil)
24
- raise MissingImplementationError, 'Subclasses must implement encode'
24
+ raise MissingImplementationError, "Subclasses must implement encode"
25
25
  end
26
26
 
27
27
  # @param stream [StringIO]
@@ -29,7 +29,7 @@ module SchemaRegistry
29
29
  # @param registry [Object]
30
30
  # @return [Object]
31
31
  def decode(_stream, _schema_text)
32
- raise MissingImplementationError, 'Subclasses must implement decode'
32
+ raise MissingImplementationError, "Subclasses must implement decode"
33
33
  end
34
34
 
35
35
  # @param message [Object]
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'schema_registry_client/schema/base'
4
- require 'schema_registry_client/output/json_schema'
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
  def self.schema_type
10
- 'JSON'
10
+ "JSON"
11
11
  end
12
12
 
13
13
  def schema_text(message, schema_name: nil)