From e13cd069269f9919b926dec4b44135f9b3a4c3de Mon Sep 17 00:00:00 2001 From: Bojan Djurkovic Date: Thu, 16 Jan 2025 11:08:31 -0400 Subject: [PATCH] backend: add more unique string usage --- backend/pkg/api/handle_schema_registry.go | 4 +-- backend/pkg/schema/client.go | 44 +++++++++++++++++++++-- backend/pkg/schema/client_test.go | 6 ++-- backend/pkg/schema/service.go | 18 +++++----- 4 files changed, 56 insertions(+), 16 deletions(-) diff --git a/backend/pkg/api/handle_schema_registry.go b/backend/pkg/api/handle_schema_registry.go index 83a92b311..955fdee91 100644 --- a/backend/pkg/api/handle_schema_registry.go +++ b/backend/pkg/api/handle_schema_registry.go @@ -726,7 +726,7 @@ func (api *API) handleCreateSchema() http.HandlerFunc { rest.SendRESTError(w, r, api.Logger, restErr) return } - if payload.Schema == "" { + if payload.Schema.Value() == "" { rest.SendRESTError(w, r, api.Logger, &rest.Error{ Err: fmt.Errorf("payload validation failed for creating schema"), Status: http.StatusBadRequest, @@ -803,7 +803,7 @@ func (api *API) handleValidateSchema() http.HandlerFunc { rest.SendRESTError(w, r, api.Logger, restErr) return } - if payload.Schema == "" { + if payload.Schema.Value() == "" { rest.SendRESTError(w, r, api.Logger, &rest.Error{ Err: fmt.Errorf("payload validation failed for validating schema"), Status: http.StatusBadRequest, diff --git a/backend/pkg/schema/client.go b/backend/pkg/schema/client.go index e4efda1de..f4cceae05 100644 --- a/backend/pkg/schema/client.go +++ b/backend/pkg/schema/client.go @@ -85,8 +85,27 @@ func newClient(cfg config.Schema) (*Client, error) { // //nolint:revive // This is stuttering when calling this with the pkg name, but without that the type SchemaResponse struct { - Schema string `json:"schema"` - References []SchemaReference `json:"references,omitempty"` + Schema unique.Handle[string] `json:"schema"` + References []SchemaReference `json:"references,omitempty"` +} + +// UnmarshalJSON for SchemaVersionedResponse +func (d *SchemaResponse) UnmarshalJSON(data []byte) error { + type Alias SchemaResponse + aux := &struct { + Schema string `json:"schema"` + *Alias + }{ + Alias: (*Alias)(d), + } + + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + d.Schema = unique.Make(aux.Schema) + + return nil } // GetSchemaByID returns the schema string identified by the input ID. @@ -609,7 +628,7 @@ func (c *Client) GetSchemas(ctx context.Context, showSoftDeleted bool) ([]*Schem // Schema is the object form of a schema for the HTTP API. type Schema struct { // Schema is the actual unescaped text of a schema. - Schema string `json:"schema"` + Schema unique.Handle[string] `json:"schema"` // Type is the type of a schema. The default type is avro. Type SchemaType `json:"schemaType,omitempty"` @@ -619,6 +638,25 @@ type Schema struct { References []SchemaReference `json:"references,omitempty"` } +// UnmarshalJSON for SchemaVersionedResponse +func (d *Schema) UnmarshalJSON(data []byte) error { + type Alias Schema + aux := &struct { + Schema string `json:"schema"` + *Alias + }{ + Alias: (*Alias)(d), + } + + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + d.Schema = unique.Make(aux.Schema) + + return nil +} + // SchemaReference is a way for a one schema to reference another. The details // for how referencing is done are type specific; for example, JSON objects // that use the key "$ref" can refer to another schema via URL. For more details diff --git a/backend/pkg/schema/client_test.go b/backend/pkg/schema/client_test.go index 5a3ce98c0..f298d55bb 100644 --- a/backend/pkg/schema/client_test.go +++ b/backend/pkg/schema/client_test.go @@ -9,13 +9,15 @@ package schema +//nolint:goimports // goimports and gofumpt conflict import ( "context" "net/http" - "testing" + "testing" //nolint:nolintlint,gofumpt // goimports and gofumpt conflict "github.com/jarcoal/httpmock" "github.com/stretchr/testify/assert" + "unique" //nolint:nolintlint,gofumpt // goimports and gofumpt conflict "github.com/redpanda-data/console/backend/pkg/config" ) @@ -43,7 +45,7 @@ func TestClient_GetSchemaByID(t *testing.T) { }) }) - expected := &SchemaResponse{Schema: schemaStr} + expected := &SchemaResponse{Schema: unique.Make(schemaStr)} actual, err := c.GetSchemaByID(context.Background(), 1000) assert.NoError(t, err, "expected no error when fetching schema by id") assert.Equal(t, expected, actual) diff --git a/backend/pkg/schema/service.go b/backend/pkg/schema/service.go index 33f114fea..5bbfc4783 100644 --- a/backend/pkg/schema/service.go +++ b/backend/pkg/schema/service.go @@ -384,7 +384,7 @@ func (s *Service) GetSchemaByID(ctx context.Context, id uint32) (*SchemaResponse // error will be returned. func (s *Service) ParseAvroSchemaWithReferences(ctx context.Context, schema *SchemaResponse, schemaCache *avro.SchemaCache) (avro.Schema, error) { if len(schema.References) == 0 { - return avro.Parse(schema.Schema) + return avro.Parse(schema.Schema.Value()) } // Fetch and parse all schema references recursively. All schemas that have @@ -398,7 +398,7 @@ func (s *Service) ParseAvroSchemaWithReferences(ctx context.Context, schema *Sch if _, err := s.ParseAvroSchemaWithReferences( ctx, &SchemaResponse{ - Schema: schemaRef.Schema.Value(), + Schema: schemaRef.Schema, References: schemaRef.References, }, schemaCache, @@ -411,7 +411,7 @@ func (s *Service) ParseAvroSchemaWithReferences(ctx context.Context, schema *Sch } // Parse the main schema in the end after solving all references - return avro.Parse(schema.Schema) + return avro.Parse(schema.Schema.Value()) } // ValidateAvroSchema tries to parse the given avro schema with the avro library. @@ -436,7 +436,7 @@ func (s *Service) ValidateJSONSchema(ctx context.Context, name string, sch Schem return fmt.Errorf("failed to retrieve reference %q: %w", ref.Subject, err) } schemaRef := Schema{ - Schema: schemaRefRes.Schema.Value(), + Schema: schemaRefRes.Schema, Type: schemaRefRes.Type, References: nil, } @@ -449,12 +449,12 @@ func (s *Service) ValidateJSONSchema(ctx context.Context, name string, sch Schem if strings.IndexByte(name, '#') != -1 { return fmt.Errorf("hashtags are not allowed as part of the schema name") } - err := schemaCompiler.AddResource(name, strings.NewReader(sch.Schema)) + err := schemaCompiler.AddResource(name, strings.NewReader(sch.Schema.Value())) if err != nil { return fmt.Errorf("failed to add resource for %q", name) } - _, err = jsonschema.CompileString(name, sch.Schema) + _, err = jsonschema.CompileString(name, sch.Schema.Value()) if err != nil { return fmt.Errorf("failed to validate schema %q: %w", name, err) } @@ -465,7 +465,7 @@ func (s *Service) ValidateJSONSchema(ctx context.Context, name string, sch Schem // along with all its references. func (s *Service) ValidateProtobufSchema(ctx context.Context, name string, sch Schema) error { schemasByPath := make(map[string]string) - schemasByPath[name] = sch.Schema + schemasByPath[name] = sch.Schema.Value() for _, ref := range sch.References { schemaRefRes, err := s.GetSchemaBySubjectAndVersion(ctx, ref.Subject, strconv.Itoa(ref.Version)) @@ -554,7 +554,7 @@ func (s *Service) GetJSONSchemaByID(ctx context.Context, schemaID uint32) (*json } func (s *Service) buildJSONSchemaWithReferences(ctx context.Context, compiler *jsonschema.Compiler, name string, schemaRes *SchemaResponse) error { - if err := compiler.AddResource(name, strings.NewReader(schemaRes.Schema)); err != nil { + if err := compiler.AddResource(name, strings.NewReader(schemaRes.Schema.Value())); err != nil { return err } @@ -567,7 +567,7 @@ func (s *Service) buildJSONSchemaWithReferences(ctx context.Context, compiler *j return err } if err := s.buildJSONSchemaWithReferences(ctx, compiler, reference.Name, &SchemaResponse{ - Schema: schemaRef.Schema.Value(), + Schema: schemaRef.Schema, References: schemaRef.References, }); err != nil { return err