Skip to content

Commit 0e76b1c

Browse files
committed
feat(schema-registry): add metadata support to schema responses
- Add SchemaMetadata struct with tags, properties, and sensitive fields - Map metadata from franz-go schema responses - Add integration tests for metadata handling
1 parent 9c84772 commit 0e76b1c

File tree

3 files changed

+123
-6
lines changed

3 files changed

+123
-6
lines changed

backend/pkg/api/handle_schema_registry_integration_test.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,3 +337,93 @@ enum MyEnumA {
337337
assert.Equal(thirdSchemaID, fourthSchemaID, "with normalize=true, schemas with different enum value order should produce the same schema ID")
338338
})
339339
}
340+
341+
func (s *APIIntegrationTestSuite) TestSchemaMetadata() {
342+
t := s.T()
343+
t.Skip() //todo remove skip once redpanda v26.1 is GA
344+
require := require.New(t)
345+
assert := assert.New(t)
346+
347+
t.Run("create schema with metadata properties", func(t *testing.T) {
348+
ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
349+
defer cancel()
350+
351+
schemaStr := `{"type":"record","name":"User","fields":[{"name":"id","type":"string"}]}`
352+
req := struct {
353+
Schema string `json:"schema"`
354+
Type string `json:"schemaType"`
355+
Metadata struct {
356+
Properties map[string]string `json:"properties"`
357+
} `json:"metadata"`
358+
}{
359+
Schema: schemaStr,
360+
Type: sr.TypeAvro.String(),
361+
}
362+
req.Metadata.Properties = map[string]string{
363+
"owner": "team-platform",
364+
"version": "1.0.0",
365+
}
366+
367+
res, body := s.apiRequest(ctx, http.MethodPost, "/api/schema-registry/subjects/test-metadata/versions", req)
368+
require.Equal(200, res.StatusCode)
369+
370+
createResponse := struct {
371+
ID int `json:"id"`
372+
}{}
373+
err := json.Unmarshal(body, &createResponse)
374+
require.NoError(err)
375+
assert.Greater(createResponse.ID, 0, "schema ID should be returned")
376+
})
377+
378+
t.Run("retrieve schema with metadata", func(t *testing.T) {
379+
ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
380+
defer cancel()
381+
382+
res, body := s.apiRequest(ctx, http.MethodGet, "/api/schema-registry/subjects/test-metadata/versions/latest", nil)
383+
require.Equal(200, res.StatusCode)
384+
385+
var details console.SchemaRegistrySubjectDetails
386+
err := json.Unmarshal(body, &details)
387+
require.NoError(err)
388+
389+
// Verify metadata is present in response
390+
require.Len(details.Schemas, 1, "should have one schema")
391+
require.NotNil(details.Schemas[0].Metadata, "metadata should not be nil")
392+
assert.Equal("team-platform", details.Schemas[0].Metadata.Properties["owner"], "owner property should match")
393+
assert.Equal("1.0.0", details.Schemas[0].Metadata.Properties["version"], "version property should match")
394+
})
395+
396+
t.Run("create schema without metadata (backward compatibility)", func(t *testing.T) {
397+
ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
398+
defer cancel()
399+
400+
schemaStr := `{"type":"record","name":"Event","fields":[{"name":"id","type":"string"}]}`
401+
req := struct {
402+
Schema string `json:"schema"`
403+
Type string `json:"schemaType"`
404+
}{
405+
Schema: schemaStr,
406+
Type: sr.TypeAvro.String(),
407+
}
408+
409+
res, body := s.apiRequest(ctx, http.MethodPost, "/api/schema-registry/subjects/test-no-metadata/versions", req)
410+
require.Equal(200, res.StatusCode)
411+
412+
createResponse := struct {
413+
ID int `json:"id"`
414+
}{}
415+
err := json.Unmarshal(body, &createResponse)
416+
require.NoError(err)
417+
assert.Greater(createResponse.ID, 0, "schema ID should be returned")
418+
419+
// Verify schema without metadata retrieves correctly
420+
res, body = s.apiRequest(ctx, http.MethodGet, "/api/schema-registry/subjects/test-no-metadata/versions/latest", nil)
421+
require.Equal(200, res.StatusCode)
422+
423+
var details console.SchemaRegistrySubjectDetails
424+
err = json.Unmarshal(body, &details)
425+
require.NoError(err)
426+
require.Len(details.Schemas, 1, "should have one schema")
427+
assert.Nil(details.Schemas[0].Metadata, "metadata should be nil for schema without metadata")
428+
})
429+
}

backend/pkg/console/schema_registry.go

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,24 @@ func mapSubjectSchema(in sr.SubjectSchema, isSoftDeleted bool) SchemaRegistryVer
186186
Version: ref.Version,
187187
}
188188
}
189+
190+
var metadata *SchemaMetadata
191+
if in.Schema.SchemaMetadata != nil {
192+
metadata = &SchemaMetadata{
193+
Tags: in.Schema.SchemaMetadata.Tags,
194+
Properties: in.Schema.SchemaMetadata.Properties,
195+
Sensitive: in.Schema.SchemaMetadata.Sensitive,
196+
}
197+
}
198+
189199
return SchemaRegistryVersionedSchema{
190200
ID: in.ID,
191201
Version: in.Version,
192202
IsSoftDeleted: isSoftDeleted,
193203
Type: in.Type,
194204
Schema: in.Schema.Schema,
195205
References: references,
206+
Metadata: metadata,
196207
}
197208
}
198209

@@ -395,12 +406,13 @@ func (s *Service) getSubjectCompatibilityLevel(ctx context.Context, srClient *rp
395406

396407
// SchemaRegistryVersionedSchema describes a retrieved schema.
397408
type SchemaRegistryVersionedSchema struct {
398-
ID int `json:"id"`
399-
Version int `json:"version"`
400-
IsSoftDeleted bool `json:"isSoftDeleted"`
401-
Type sr.SchemaType `json:"type"`
402-
Schema string `json:"schema"`
403-
References []Reference `json:"references"`
409+
ID int `json:"id"`
410+
Version int `json:"version"`
411+
IsSoftDeleted bool `json:"isSoftDeleted"`
412+
Type sr.SchemaType `json:"type"`
413+
Schema string `json:"schema"`
414+
References []Reference `json:"references"`
415+
Metadata *SchemaMetadata `json:"metadata,omitempty"`
404416
}
405417

406418
// Reference describes a reference to a different schema stored in the schema registry.
@@ -410,6 +422,13 @@ type Reference struct {
410422
Version int `json:"version"`
411423
}
412424

425+
// SchemaMetadata contains metadata associated with a schema version.
426+
type SchemaMetadata struct {
427+
Tags map[string][]string `json:"tags,omitempty"`
428+
Properties map[string]string `json:"properties,omitempty"`
429+
Sensitive []string `json:"sensitive,omitempty"`
430+
}
431+
413432
// GetSchemaRegistrySchema retrieves a schema for a given subject, version tuple from the
414433
// schema registry. You can use -1 as the version to return the latest schema,
415434
func (s *Service) GetSchemaRegistrySchema(ctx context.Context, subjectName string, version int, showSoftDeleted bool) (*SchemaRegistryVersionedSchema, error) {

frontend/src/state/rest-interfaces.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,6 +1518,7 @@ export type SchemaRegistryVersionedSchema = {
15181518
type: SchemaTypeType;
15191519
schema: string;
15201520
references: SchemaReference[];
1521+
metadata?: SchemaMetadata;
15211522
};
15221523

15231524
export type SchemaReference = {
@@ -1526,6 +1527,12 @@ export type SchemaReference = {
15261527
version: number;
15271528
};
15281529

1530+
export type SchemaMetadata = {
1531+
tags?: Record<string, string[]>;
1532+
properties?: Record<string, string>;
1533+
sensitive?: string[];
1534+
};
1535+
15291536
// DELETE /schema-registry/subjects/{subject}/versions/{version}
15301537
export type SchemaRegistryDeleteSubjectVersionResponse = {
15311538
deletedVersion: number;
@@ -1541,6 +1548,7 @@ export type SchemaRegistryCreateSchema = {
15411548
schema: string;
15421549
schemaType: SchemaTypeType;
15431550
references: SchemaReference[];
1551+
metadata?: SchemaMetadata;
15441552
params?: {
15451553
normalize?: boolean;
15461554
};

0 commit comments

Comments
 (0)