package dbx import ( "encoding/json" "fmt" "io" "log/slog" "github.com/segmentio/ksuid" ) // UUID is a wrapper around ksuid.KSUID that implements the // graphql.Unmarshaler and graphql.Marshaler interfaces for use in GraphQL APIs. // It also provides additional convenience functions such as ParseUUID and NewUUID. type UUID struct { ksuid.KSUID } type uuidTransport struct { UUIDStr string `json:"uuid_str"` } // Generates a new, wrapped KSUID. In the strange case that random bytes can't be read, it will panic. func NewUUID() UUID { return UUID{KSUID: ksuid.New()} } // ParseUUID parses a UUID from a string. If the string is not a valid UUID, it will return an error. func ParseUUID(s string) (UUID, error) { ksuid, err := ksuid.Parse(s) if err != nil { return UUID{}, err } return UUID{KSUID: ksuid}, nil } // UnmarshalGQL implements the graphql.Unmarshaler interface func (u *UUID) UnmarshalGQL(value interface{}) error { slog.Debug("uuid unmarshaling from gql", "val", value) str, ok := value.(string) if !ok { return fmt.Errorf("GraphQL failed to unmarshal UUID value: %v", value) } if err := u.UnmarshalText([]byte(str)); err != nil { return err } return nil } // MarshalGQL implements the graphql.Marshaler interface func (u UUID) MarshalGQL(w io.Writer) { transport := uuidTransport{UUIDStr: u.String()} json, err := json.Marshal(transport) if err != nil { panic(fmt.Errorf("GraphQL failed to JSON-marshal UUID value: %s", err)) } slog.Debug("uuid marshaling to gql", "uuid", u.String(), "json", string(json)) _, err = w.Write(json) if err != nil { panic(fmt.Errorf("GraphQL failed to write UUID value: %s", string(json))) } }