From 75c3162552a84a68ee3ed1bbeb106f7efb0cc96c Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Thu, 13 Aug 2026 13:34:21 -0400 Subject: [PATCH] fix: only apply the schema definition prefix when explicitly specified Schema definition prefixes were auto-detected: `schema write` and `import` read the existing schema from the server, extracted the common prefix from its definitions, and applied that prefix to the schema being written. This existed to support Serverless, which is gone, and it means writing an unprefixed schema to a permissions system with a prefixed one silently rewrites the definitions. Remove `determinePrefixForSchema` so the prefix is applied only when `--schema-definition-prefix` is passed. The flag continues to work exactly as before when specified. `schema copy` is unaffected in behavior: the prefix it inferred came from the source schema, whose definitions already carry it, and `compiler.ObjectTypePrefix` does not re-prefix an already-prefixed definition. It now skips a redundant compile. `schema write` and `import` each drop a ReadSchema round-trip. `commands.ReadSchema` had no remaining callers and is removed. --- docs/zed.md | 6 +-- internal/cmd/import.go | 7 +--- internal/cmd/schema.go | 75 ++-------------------------------- internal/cmd/schema_test.go | 81 +++++++++---------------------------- internal/commands/schema.go | 23 ----------- 5 files changed, 29 insertions(+), 163 deletions(-) diff --git a/docs/zed.md b/docs/zed.md index ee21bb0c..f2d5b116 100644 --- a/docs/zed.md +++ b/docs/zed.md @@ -509,7 +509,7 @@ zed import [flags] --batch-size int import batch size (default 1000) --relationships import relationships (default true) --schema import schema (default true) - --schema-definition-prefix string prefix to add to the schema's definition(s) before importing + --schema-definition-prefix string prefix to add to the schema's definition(s) before importing; no prefix is added unless specified --workers int number of concurrent batching workers (default 1) ``` @@ -1233,7 +1233,7 @@ zed schema copy [flags] ``` --json output as JSON - --schema-definition-prefix string prefix to add to the schema's definition(s) before writing + --schema-definition-prefix string prefix to add to the schema's definition(s) before writing; no prefix is added unless specified ``` ### Options Inherited From Parent Flags @@ -1344,7 +1344,7 @@ zed schema write [flags] ``` --json output as JSON - --schema-definition-prefix string prefix to add to the schema's definition(s) before writing + --schema-definition-prefix string prefix to add to the schema's definition(s) before writing; no prefix is added unless specified ``` ### Options Inherited From Parent Flags diff --git a/internal/cmd/import.go b/internal/cmd/import.go index 8ce71fb9..de63f0e5 100644 --- a/internal/cmd/import.go +++ b/internal/cmd/import.go @@ -57,10 +57,7 @@ func registerImportCmd(rootCmd *cobra.Command) { if err != nil { return err } - prefix, err := determinePrefixForSchema(cmd.Context(), cobrautil.MustGetString(cmd, "schema-definition-prefix"), client, nil) - if err != nil { - return err - } + prefix := cobrautil.MustGetString(cmd, "schema-definition-prefix") log.Trace().Msgf("using prefix: %s", prefix) return importCmdFunc(cmd, client, client, prefix, args[0]) }, @@ -71,7 +68,7 @@ func registerImportCmd(rootCmd *cobra.Command) { importCmd.Flags().Int("workers", 1, "number of concurrent batching workers") importCmd.Flags().Bool("schema", true, "import schema") importCmd.Flags().Bool("relationships", true, "import relationships") - importCmd.Flags().String("schema-definition-prefix", "", "prefix to add to the schema's definition(s) before importing") + importCmd.Flags().String("schema-definition-prefix", "", "prefix to add to the schema's definition(s) before importing; no prefix is added unless specified") } func importCmdFunc(cmd *cobra.Command, schemaClient v1.SchemaServiceClient, relationshipsClient v1.PermissionsServiceClient, prefix, filename string) error { diff --git a/internal/cmd/schema.go b/internal/cmd/schema.go index 031117fd..f87adc9b 100644 --- a/internal/cmd/schema.go +++ b/internal/cmd/schema.go @@ -7,11 +7,9 @@ import ( "io" "os" "path/filepath" - "strings" "github.com/ccoveille/go-safecast/v2" "github.com/jzelinskie/cobrautil/v2" - "github.com/jzelinskie/stringz" "github.com/rs/zerolog/log" "github.com/spf13/cobra" "golang.org/x/term" @@ -95,11 +93,11 @@ func registerAdditionalSchemaCmds(schemaCmd *cobra.Command) { schemaCmd.AddCommand(schemaCopyCmd) schemaCopyCmd.Flags().Bool("json", false, "output as JSON") - schemaCopyCmd.Flags().String("schema-definition-prefix", "", "prefix to add to the schema's definition(s) before writing") + schemaCopyCmd.Flags().String("schema-definition-prefix", "", "prefix to add to the schema's definition(s) before writing; no prefix is added unless specified") schemaCmd.AddCommand(schemaWriteCmd) schemaWriteCmd.Flags().Bool("json", false, "output as JSON") - schemaWriteCmd.Flags().String("schema-definition-prefix", "", "prefix to add to the schema's definition(s) before writing") + schemaWriteCmd.Flags().String("schema-definition-prefix", "", "prefix to add to the schema's definition(s) before writing; no prefix is added unless specified") schemaCmd.AddCommand(schemaDiffCmd) @@ -230,12 +228,7 @@ func schemaCopyInner(ctx context.Context, srcClient, destClient v1.SchemaService } log.Trace().Interface("response", readResp).Msg("read schema") - prefix, err := determinePrefixForSchema(ctx, definitionPrefix, nil, &readResp.SchemaText) - if err != nil { - return nil, err - } - - schemaText, err := rewriteSchema(ctx, readResp.SchemaText, prefix) + schemaText, err := rewriteSchema(ctx, readResp.SchemaText, definitionPrefix) if err != nil { return nil, err } @@ -284,11 +277,7 @@ func schemaWriteCmdImpl(cmd *cobra.Command, args []string, client v1.SchemaServi return errors.New("attempted to write empty schema") } - prefix, err := determinePrefixForSchema(cmd.Context(), cobrautil.MustGetString(cmd, "schema-definition-prefix"), client, nil) - if err != nil { - return err - } - + prefix := cobrautil.MustGetString(cmd, "schema-definition-prefix") schemaText, err := rewriteSchema(cmd.Context(), string(schemaBytes), prefix) if err != nil { return err @@ -334,62 +323,6 @@ func rewriteSchema(ctx context.Context, existingSchemaText string, definitionPre return generated, err } -// determinePrefixForSchema determines the prefix to be applied to a schema that will be written. -// -// If specifiedPrefix is non-empty, it is returned immediately. -// If existingSchema is non-nil, it is parsed for the prefix. -// Otherwise, the client is used to retrieve the existing schema (if any), and the prefix is retrieved from there. -func determinePrefixForSchema(ctx context.Context, specifiedPrefix string, client v1.SchemaServiceClient, existingSchema *string) (string, error) { - if specifiedPrefix != "" { - return specifiedPrefix, nil - } - - var schemaText string - if existingSchema != nil { - schemaText = *existingSchema - } else { - readSchemaText, err := commands.ReadSchema(ctx, client) - if err != nil { - return "", nil - } - schemaText = readSchemaText - } - - // If there is no schema found, return the empty string. - if schemaText == "" { - return "", nil - } - - // Otherwise, compile the schema and grab the prefixes of the namespaces defined. - found, err := compiler.Compile( - compiler.InputSchema{Source: input.Source("schema"), SchemaString: schemaText}, - compiler.AllowUnprefixedObjectType(), - compiler.SkipValidation(), - ) - if err != nil { - return "", err - } - - foundPrefixes := make([]string, 0, len(found.OrderedDefinitions)) - for _, def := range found.OrderedDefinitions { - if strings.Contains(def.GetName(), "/") { - parts := strings.Split(def.GetName(), "/") - foundPrefixes = append(foundPrefixes, parts[0]) - } else { - foundPrefixes = append(foundPrefixes, "") - } - } - - prefixes := stringz.Dedup(foundPrefixes) - if len(prefixes) == 1 { - prefix := prefixes[0] - log.Debug().Str("prefix", prefix).Msg("found schema definition prefix") - return prefix, nil - } - - return "", nil -} - func schemaCompileOuter(cmd *cobra.Command, args []string) (bool, error) { outputFilepath := cobrautil.MustGetString(cmd, "out") diff --git a/internal/cmd/schema_test.go b/internal/cmd/schema_test.go index 80d4b884..607684ce 100644 --- a/internal/cmd/schema_test.go +++ b/internal/cmd/schema_test.go @@ -23,64 +23,6 @@ import ( "github.com/authzed/zed/internal/zedtesting" ) -func TestDeterminePrefixForSchema(t *testing.T) { - tests := []struct { - name string - existingSchema string - specifiedPrefix string - expectedPrefix string - }{ - { - "empty schema", - "", - "", - "", - }, - { - "no prefix, none specified", - `definition user {}`, - "", - "", - }, - { - "no prefix, one specified", - `definition user {}`, - "test", - "test", - }, - { - "prefix found", - `definition test/user {}`, - "", - "test", - }, - { - "multiple prefixes found", - `definition test/user {} - - definition something/resource {}`, - "", - "", - }, - { - "multiple prefixes found, one specified", - `definition test/user {} - - definition something/resource {}`, - "foobar", - "foobar", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - found, err := determinePrefixForSchema(t.Context(), test.specifiedPrefix, nil, &test.existingSchema) - require.NoError(t, err) - require.Equal(t, test.expectedPrefix, found) - }) - } -} - func TestRewriteSchema(t *testing.T) { tests := []struct { name string @@ -366,6 +308,7 @@ func TestSchemaWrite(t *testing.T) { testCases := map[string]struct { schemaMakerFn func() ([]string, error) terminalChecker *mockTermChecker + existingSchema string expectErr string expectSchemaWritten string }{ @@ -397,6 +340,20 @@ definition resource { terminalChecker: &mockTermChecker{returnVal: false}, expectSchemaWritten: "definition user{}\ndefinition document { relation read: user }", }, + `existing_prefixed_schema_does_not_prefix`: { + schemaMakerFn: func() ([]string, error) { + return []string{ + filepath.Join("write-schema-test", "basic.zed"), + }, nil + }, + existingSchema: `definition someprefix/user {}`, + expectSchemaWritten: `definition user {} +definition resource { + relation view: user + permission viewer = view +}`, + terminalChecker: &mockTermChecker{returnVal: false}, + }, `schema_from_stdin_but_terminal`: { schemaMakerFn: func() ([]string, error) { schemaContent := "definition user{}\ndefinition document { relation read: user }" @@ -450,11 +407,13 @@ definition resource { defer ctrl.Finish() mockClient := NewMockSchemaServiceClient(ctrl) - // ReadSchema is always called at least once + // Writing never consults the existing schema: the prefix is applied only when + // explicitly specified via --schema-definition-prefix. Serving the existing schema + // here ensures a reintroduced prefix inference would be caught by the assertions below. mockClient.EXPECT(). ReadSchema(gomock.Any(), gomock.Any()). - Return(&v1.ReadSchemaResponse{SchemaText: ""}, nil). - MaxTimes(2) // sometimes we read for prefix determination + Return(&v1.ReadSchemaResponse{SchemaText: tc.existingSchema}, nil). + AnyTimes() // Set up WriteSchema expectations based on test case var receivedSchema string diff --git a/internal/commands/schema.go b/internal/commands/schema.go index e810ef46..13d19275 100644 --- a/internal/commands/schema.go +++ b/internal/commands/schema.go @@ -1,14 +1,10 @@ package commands import ( - "context" - "github.com/jzelinskie/cobrautil/v2" "github.com/jzelinskie/stringz" "github.com/rs/zerolog/log" "github.com/spf13/cobra" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" v1 "github.com/authzed/authzed-go/proto/authzed/api/v1" @@ -63,22 +59,3 @@ func schemaReadCmdFunc(cmd *cobra.Command, _ []string) error { console.Println(stringz.Join("\n\n", resp.SchemaText)) return nil } - -// ReadSchema calls read schema for the client and returns the schema found. -func ReadSchema(ctx context.Context, client v1.SchemaServiceClient) (string, error) { - request := &v1.ReadSchemaRequest{} - log.Trace().Interface("request", request).Msg("requesting schema read") - - resp, err := client.ReadSchema(ctx, request) - if err != nil { - errStatus, ok := status.FromError(err) - if !ok || errStatus.Code() != codes.NotFound { - return "", err - } - - log.Debug().Msg("no schema defined") - return "", nil - } - - return resp.SchemaText, nil -}