diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e8ee03..702702e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Breaking changes - Removed `UUID`, `NewUUID`, and `ParseUUID`. -- Added `StringKSUID` and `BinaryKSUID` with distinct SQL encodings and shared GraphQL transit (`ksuid_str` JSON field, replacing `uuid_str`). +- Added `StringKSUID` and `BinaryKSUID` with distinct SQL encodings and shared GraphQL string scalar transit. - Removed `DBConfig.AutoMigrate`, `SQLOFunc`, and `JSONB.ToMap()`. - Replaced `Column` / `ColumnList` intersection interfaces with dialect-neutral type aliases. - `Apply*` and `Expr*` helpers now accept `Column` / `ColumnList` / `Expression` aliases instead of `mysql.*` types. diff --git a/README.md b/README.md index 5126404..8bf0378 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ For Postgres, use `dbx.DialectPostgres` and blank-import `dbxp` instead of `dbxm | `StringKSUID` | `VARCHAR(27)`, `TEXT` | Base62 string | | `BinaryKSUID` | `BINARY(20)`, `BYTEA` | Raw 20 bytes | -Both types use identical GraphQL JSON transit (`ksuid_str` field). Pick the type that matches your column encoding — Scan rejects ambiguous payloads. +Both types use identical GraphQL string scalar transit. Pick the type that matches your column encoding — Scan rejects ambiguous payloads. ## Dialect notes diff --git a/doc.go b/doc.go index 54e2e2d..5329520 100644 --- a/doc.go +++ b/doc.go @@ -23,7 +23,7 @@ // # Identifier types // // [StringKSUID] and [BinaryKSUID] wrap segmentio/ksuid with storage-specific -// SQL encoding. Both share identical GraphQL string transit. Choose StringKSUID +// SQL encoding. Both share identical GraphQL string scalar transit. Choose StringKSUID // for text columns (VARCHAR, TEXT); choose BinaryKSUID for binary columns // (BINARY(20), BYTEA). // diff --git a/ksuid_binary.go b/ksuid_binary.go index 620eda9..c30d043 100644 --- a/ksuid_binary.go +++ b/ksuid_binary.go @@ -9,8 +9,8 @@ import ( ) // BinaryKSUID wraps segmentio/ksuid for binary-column storage (BINARY(20), BYTEA). -// SQL Scan/Value use the raw 20-byte encoding. GraphQL transit uses the shared -// string JSON transport (see ksuid_gql.go). +// SQL Scan/Value use the raw 20-byte encoding. GraphQL transit uses a string +// scalar (base62 KSUID). type BinaryKSUID struct { ksuid.KSUID } diff --git a/ksuid_gql.go b/ksuid_gql.go index 80d4cfc..8213020 100644 --- a/ksuid_gql.go +++ b/ksuid_gql.go @@ -1,17 +1,13 @@ package dbx import ( - "encoding/json" "fmt" "io" + "strconv" "github.com/segmentio/ksuid" ) -type ksuidGQLTransport struct { - KSUIDStr string `json:"ksuid_str"` -} - func unmarshalKSUIDFromGQL(value any, unmarshal func([]byte) error) error { str, ok := value.(string) if !ok { @@ -20,14 +16,11 @@ func unmarshalKSUIDFromGQL(value any, unmarshal func([]byte) error) error { return unmarshal([]byte(str)) } +// marshalKSUIDToGQL writes a JSON-encoded string scalar for gqlgen. func marshalKSUIDToGQL(w io.Writer, id ksuid.KSUID) { - transport := ksuidGQLTransport{KSUIDStr: id.String()} - data, err := json.Marshal(transport) - if err != nil { - panic(fmt.Errorf("GraphQL failed to JSON-marshal KSUID value: %s", err)) - } - if _, err := w.Write(data); err != nil { - panic(fmt.Errorf("GraphQL failed to write KSUID value: %s", string(data))) + quoted := strconv.Quote(id.String()) + if _, err := io.WriteString(w, quoted); err != nil { + panic(fmt.Errorf("GraphQL failed to write KSUID value: %w", err)) } } diff --git a/ksuid_string.go b/ksuid_string.go index bc6b87d..f1e28db 100644 --- a/ksuid_string.go +++ b/ksuid_string.go @@ -9,8 +9,8 @@ import ( ) // StringKSUID wraps segmentio/ksuid for text-column storage (VARCHAR, TEXT). -// SQL Scan/Value use the base62 string encoding. GraphQL transit uses the -// shared string JSON transport (see ksuid_gql.go). +// SQL Scan/Value use the base62 string encoding. GraphQL transit uses a string +// scalar (base62 KSUID). type StringKSUID struct { ksuid.KSUID } diff --git a/ksuid_test.go b/ksuid_test.go index a7995cb..6f5bad5 100644 --- a/ksuid_test.go +++ b/ksuid_test.go @@ -3,6 +3,7 @@ package dbx import ( "bytes" "encoding/json" + "io" "testing" "github.com/segmentio/ksuid" @@ -50,16 +51,32 @@ func TestStringKSUID_Conversion(t *testing.T) { func TestStringKSUID_GQL(t *testing.T) { id := NewStringKSUID() + wire := assertKSUIDGQLMarshal(t, id) + + var parsed StringKSUID + require.NoError(t, parsed.UnmarshalGQL(wire)) + assert.Equal(t, id, parsed) +} + +func TestBinaryKSUID_GQL(t *testing.T) { + id := NewBinaryKSUID() + wire := assertKSUIDGQLMarshal(t, id) + + var parsed BinaryKSUID + require.NoError(t, parsed.UnmarshalGQL(wire)) + assert.Equal(t, id, parsed) +} + +func assertKSUIDGQLMarshal(t *testing.T, id interface{ String() string; MarshalGQL(w io.Writer) }) string { + t.Helper() + var buf bytes.Buffer id.MarshalGQL(&buf) - var transport ksuidGQLTransport - require.NoError(t, json.Unmarshal(buf.Bytes(), &transport)) - assert.Equal(t, id.String(), transport.KSUIDStr) - - var parsed StringKSUID - require.NoError(t, parsed.UnmarshalGQL(id.String())) - assert.Equal(t, id, parsed) + var wire string + require.NoError(t, json.Unmarshal(buf.Bytes(), &wire)) + assert.Equal(t, id.String(), wire) + return wire } func TestBinaryKSUID_ScanValue(t *testing.T) { @@ -100,19 +117,6 @@ func TestBinaryKSUID_Conversion(t *testing.T) { assert.Equal(t, b, s.AsBinaryKSUID()) } -func TestBinaryKSUID_GQL(t *testing.T) { - id := NewBinaryKSUID() - var buf bytes.Buffer - id.MarshalGQL(&buf) - - var transport ksuidGQLTransport - require.NoError(t, json.Unmarshal(buf.Bytes(), &transport)) - assert.Equal(t, id.String(), transport.KSUIDStr) - - var parsed BinaryKSUID - require.NoError(t, parsed.UnmarshalGQL(id.String())) - assert.Equal(t, id, parsed) -} func TestParseStringKSUID(t *testing.T) { id := ksuid.New()