add utilities, JSONB & UUID types
This commit is contained in:
59
jsonb.go
Normal file
59
jsonb.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package dbx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// JSONB is a type that represents a JSON object stored in the database. It is
|
||||||
|
// a map[string]any and implements sql.Scanner, driver.Valuer, and Stringer.
|
||||||
|
type JSONB map[string]any
|
||||||
|
|
||||||
|
// NewJSONB creates a new JSONB value from a map[string]any.
|
||||||
|
func NewJSONB(data map[string]any) JSONB {
|
||||||
|
return JSONB(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToMap converts the JSONB value to a map[string]any.
|
||||||
|
func (j JSONB) ToMap() map[string]any {
|
||||||
|
return map[string]any(j)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan implements the sql.Scanner interface. It supports converting from
|
||||||
|
// string, []byte, or nil into a JSONB value. Attempting to convert from
|
||||||
|
// any other type will return an error.
|
||||||
|
func (j *JSONB) Scan(src any) error {
|
||||||
|
switch v := src.(type) {
|
||||||
|
case nil:
|
||||||
|
*j = nil
|
||||||
|
return nil
|
||||||
|
case []byte:
|
||||||
|
return json.Unmarshal(v, j)
|
||||||
|
case string:
|
||||||
|
return json.Unmarshal([]byte(v), j)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Scan: unable to scan type %T into JSONB", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the driver.Valuer interface. It converts the JSONB
|
||||||
|
// value into a SQL driver value which can be used to directly use the
|
||||||
|
// JSONB as a parameter to a SQL query.
|
||||||
|
func (j JSONB) Value() (driver.Value, error) {
|
||||||
|
if j == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal the JSONB to a byte slice
|
||||||
|
return json.Marshal(j)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j JSONB) String() string {
|
||||||
|
// Marshal the JSONB to a byte slice
|
||||||
|
bytes, err := json.Marshal(j)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
23
utility.go
Normal file
23
utility.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package dbx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-jet/jet/v2/mysql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExprID converts a list of uint64 values to a list of mysql.Expression values
|
||||||
|
func ExprID(ids []uint64) []mysql.Expression {
|
||||||
|
expressions := make([]mysql.Expression, len(ids))
|
||||||
|
for i, id := range ids {
|
||||||
|
expressions[i] = mysql.Uint64(id)
|
||||||
|
}
|
||||||
|
return expressions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExprEnum converts a list of uint8 values to a list of mysql.Expression values
|
||||||
|
func ExprEnum(enums []uint8) []mysql.Expression {
|
||||||
|
expressions := make([]mysql.Expression, len(enums))
|
||||||
|
for i, enum := range enums {
|
||||||
|
expressions[i] = mysql.Uint8(enum)
|
||||||
|
}
|
||||||
|
return expressions
|
||||||
|
}
|
||||||
64
uuid.go
Normal file
64
uuid.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user