refactor: use GraphQL string scalar for KSUID transit
Marshal and unmarshal both use the base62 KSUID string, replacing the legacy JSON object wrapper and aligning gqlgen scalar behavior. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+1
-1
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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).
|
||||
//
|
||||
|
||||
+2
-2
@@ -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
|
||||
}
|
||||
|
||||
+5
-12
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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
|
||||
}
|
||||
|
||||
+24
-20
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user