-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit c82bd00
Showing
12 changed files
with
550 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# SurrealDB Go Utils | ||
|
||
A basic little package with some useful structs for working with surreal | ||
|
||
### Thing: | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
"github.com/idevelopthings/surrealdb_go_utils" | ||
) | ||
|
||
type User struct { | ||
ID surrealdb_go_utils.Thing `json:"id"` | ||
} | ||
``` | ||
|
||
Thing will allow you to the regular surrealdb id formatting, and take out the Table or Value with ease. | ||
We can also use this for situations in queries. | ||
|
||
It will also unescape the value for you, so when it's passed via an api endpoint as a normal id, like "user:u4y127h7fh78h1", it will automatically be unescaped. | ||
|
||
### Record: | ||
|
||
This is useful for when you have a type for your Table, which will have a pointer value, for example. A Book with an Author: | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
"github.com/idevelopthings/surrealdb_go_utils" | ||
) | ||
|
||
type Author struct { | ||
ID surrealdb_go_utils.Thing `json:"id"` | ||
} | ||
type Book struct { | ||
ID surrealdb_go_utils.Thing `json:"id"` | ||
Author surrealdb_go_utils.Record[Author] `json:"author"` | ||
} | ||
|
||
``` | ||
|
||
When you pull the Book from the db as normal, it will only return an id string, something like `{"id": "book:u4y127h7fh78h1", "author": "author:jsdfhf8f2h7fh"}`. | ||
|
||
When you also fetch "author", this usually will cause issues unmarsalling the json. | ||
|
||
But this "Record" will handle both situations, and marshal to json as the correct type | ||
|
||
```go | ||
var book *Book | ||
|
||
book.Author.IsId() // Returns true if the value is an id(record pointer) | ||
book.Author.Id() // Returns the ID of the record | ||
book.Author.IsRecord() // Returns true if it's the Author value/record | ||
book.Author.Record() // Returns the Author record(if the query included `fetch author` for example) | ||
|
||
``` | ||
|
||
But when you use the Record, it will automatically unescape the id, and then pull the Author from the db, and return the Author struct. | ||
|
||
### RecordList | ||
|
||
This works the same as "Record" above, but for an array instead. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module github.com/idevelopthings/surrealdb_go_utils | ||
|
||
go 1.19 | ||
|
||
require ( | ||
github.com/buger/jsonparser v1.1.1 | ||
github.com/goccy/go-json v0.9.11 | ||
golang.org/x/exp v0.0.0-20221002003631-540bb7301a08 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= | ||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= | ||
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= | ||
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= | ||
golang.org/x/exp v0.0.0-20221002003631-540bb7301a08 h1:LtBIgSqNhkuC9gA3BFjGy5obHQT1lnmNsMDFSqWzQ5w= | ||
golang.org/x/exp v0.0.0-20221002003631-540bb7301a08/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package surrealdb_go_utils | ||
|
||
import ( | ||
"github.com/buger/jsonparser" | ||
"github.com/goccy/go-json" | ||
"golang.org/x/exp/maps" | ||
) | ||
|
||
type ListType string | ||
|
||
var ( | ||
ListTypeIds ListType = "ids" | ||
ListTypeObjects ListType = "objects" | ||
) | ||
|
||
type RecordList[T any | string] struct { | ||
items []T | ||
ids map[string]*T | ||
listType ListType | ||
} | ||
|
||
func (d *RecordList[T]) MarshalJSON() ([]byte, error) { | ||
if d.IsResolved() { | ||
return json.Marshal(d.items) | ||
} | ||
|
||
return json.Marshal(d.Ids()) | ||
} | ||
|
||
func (d *RecordList[T]) UnmarshalJSON(data []byte) error { | ||
_, dataType, _, err := jsonparser.Get(data) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if dataType == jsonparser.Array { | ||
dat := *new(RecordList[T]) | ||
dat.ids = make(map[string]*T) | ||
dat.items = make([]T, 0) | ||
dat.listType = "" | ||
|
||
jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) { | ||
if dataType == jsonparser.String { | ||
val, err := jsonparser.ParseString(value) | ||
if err != nil { | ||
panic(err) | ||
} | ||
dat.ids[val] = nil | ||
dat.listType = ListTypeIds | ||
} else { | ||
var item T = *new(T) | ||
err = json.Unmarshal(value, &item) | ||
if err != nil { | ||
return | ||
} | ||
dat.items = append(dat.items, item) | ||
dat.listType = ListTypeObjects | ||
} | ||
}) | ||
|
||
*d = dat | ||
|
||
return nil | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// IsResolved check if the list has been resolved/initiated | ||
func (d *RecordList[T]) IsResolved() bool { | ||
return d.listType != ListTypeIds | ||
} | ||
|
||
// Items returns the items in the list | ||
func (d *RecordList[T]) Items() []T { | ||
return d.items | ||
} | ||
|
||
// IdMap returns the ids in the list(this would be used when we haven't fetched the records) | ||
func (d *RecordList[T]) IdMap() map[string]*T { | ||
return d.ids | ||
} | ||
|
||
// Ids return an array of all ids in the list | ||
func (d *RecordList[T]) Ids() []string { | ||
return maps.Keys(d.ids) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package surrealdb_go_utils | ||
|
||
import ( | ||
"github.com/buger/jsonparser" | ||
"github.com/goccy/go-json" | ||
) | ||
|
||
type RecordType string | ||
|
||
var ( | ||
RecordTypeId RecordType = "id" | ||
RecordTypeRecord RecordType = "record" | ||
) | ||
|
||
type Record[T any] struct { | ||
id string | ||
value T | ||
isSet bool | ||
kind RecordType | ||
} | ||
|
||
func (record *Record[T]) UnmarshalJSON(data []byte) error { | ||
_, dataType, _, err := jsonparser.Get(data) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
dat := *new(Record[T]) | ||
|
||
if dataType == jsonparser.String { | ||
dat.kind = RecordTypeId | ||
dat.id, err = jsonparser.GetString(data) | ||
if err != nil { | ||
return err | ||
} | ||
dat.isSet = true | ||
|
||
*record = dat | ||
|
||
return nil | ||
} | ||
if dataType == jsonparser.Object { | ||
var item T = *new(T) | ||
err = json.Unmarshal(data, &item) | ||
if err != nil { | ||
return err | ||
} | ||
dat.value = item | ||
dat.kind = RecordTypeRecord | ||
dat.isSet = true | ||
|
||
dat.id, err = jsonparser.GetString(data, "id") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
*record = dat | ||
|
||
return nil | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (record *Record[T]) MarshalJSON() ([]byte, error) { | ||
if record.IsId() { | ||
return []byte(`"` + record.id + `"`), nil | ||
} | ||
|
||
return json.Marshal(record.value) | ||
} | ||
|
||
// IsId returns true if the record is an id | ||
func (record *Record[T]) IsId() bool { | ||
return record.kind == RecordTypeId | ||
} | ||
|
||
// Id returns the id of the record(used when we haven't fetched the record yet) | ||
func (record *Record[T]) Id() string { | ||
return record.id | ||
} | ||
|
||
// IsRecord returns true if we're using record rather than id | ||
func (record *Record[T]) IsRecord() bool { | ||
return record.kind == RecordTypeRecord | ||
} | ||
|
||
// Record returns the record | ||
func (record *Record[T]) Record() T { | ||
return record.value | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package surrealdb_go_utils | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/goccy/go-json" | ||
) | ||
|
||
func TestFetchedRecordUnmarshal(t *testing.T) { | ||
type subrec struct { | ||
Id *Thing `json:"id"` | ||
} | ||
type rec struct { | ||
Id *Thing `json:"id"` | ||
Value *Record[subrec] | ||
} | ||
|
||
jsonWithFetch := `{"id": "user:one", "value": {"id": "book:one"}}` | ||
var r rec | ||
err := json.Unmarshal([]byte(jsonWithFetch), &r) | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
|
||
if r.Value.IsRecord() == false { | ||
t.Error("Invalid value") | ||
} | ||
|
||
if r.Value.Id() != "book:one" { | ||
t.Error("Invalid value") | ||
} | ||
|
||
if r.Value.Record().Id.Value() != "book:one" { | ||
t.Error("Invalid value") | ||
} | ||
} | ||
|
||
func TestRecordPointerUnmarshal(t *testing.T) { | ||
type subrec struct { | ||
Id *Thing `json:"id"` | ||
} | ||
type rec struct { | ||
Id *Thing `json:"id"` | ||
Value *Record[subrec] | ||
} | ||
|
||
jsonWithFetch := `{"id": "user:one", "value": "book:one"}` | ||
var r rec | ||
err := json.Unmarshal([]byte(jsonWithFetch), &r) | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
|
||
if r.Value.IsId() == false { | ||
t.Error("Invalid value") | ||
} | ||
|
||
if r.Value.Id() != "book:one" { | ||
t.Error("Invalid value") | ||
} | ||
} |
Oops, something went wrong.