Skip to content

Commit

Permalink
feat(marshal): smartUnmarshalRaw1 to handle raw query single return r…
Browse files Browse the repository at this point in the history
…esult including THROW statement errors
  • Loading branch information
agufagit committed Jan 1, 2024
1 parent 93dda5f commit 1d01200
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 3 deletions.
47 changes: 44 additions & 3 deletions db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ func (s *SurrealDBTestSuite) TestDelete() {

func (s *SurrealDBTestSuite) TestInsert() {
s.Run("raw map works", func() {
userData, err := s.db.Insert("user", map[string]interface{}{
userData, err := s.db.Insert("users", map[string]interface{}{
"username": "johnny",
"password": "123",
})
Expand All @@ -244,7 +244,7 @@ func (s *SurrealDBTestSuite) TestInsert() {
})

s.Run("Single insert works", func() {
userData, err := s.db.Insert("user", testUser{
userData, err := s.db.Insert("users", testUser{
Username: "johnny",
Password: "123",
})
Expand All @@ -268,7 +268,7 @@ func (s *SurrealDBTestSuite) TestInsert() {
Username: "johnny2",
Password: "123",
})
userData, err := s.db.Insert("user", userInsert)
userData, err := s.db.Insert("users", userInsert)
s.Require().NoError(err)

// unmarshal the data into a user struct
Expand Down Expand Up @@ -574,6 +574,47 @@ func (s *SurrealDBTestSuite) TestSmartUnMarshalQuery() {
})
}

func (s *SurrealDBTestSuite) TestSmartUnMarshalRaw1Query() {
user := testUser{
Username: "electwix",
Password: "1234",
}

s.Run("raw create query", func() {
QueryStr := "Create Only users set Username = $user, Password = $pass"
data, err := marshal.SmartUnmarshalRaw1[testUser](s.db.Query(QueryStr, map[string]interface{}{
"user": user.Username,
"pass": user.Password,
}))

s.Require().NoError(err)
s.Equal("electwix", data.Username)
user = data
})

s.Run("raw select query", func() {
dataArr, err := marshal.SmartUnmarshalRaw1[[]testUser](s.db.Query("Select * from $record", map[string]interface{}{
"record": user.ID,
}))

s.Require().NoError(err)
s.Equal("electwix", dataArr[0].Username)
})

s.Run("raw THROW query", func() {
_, err := marshal.SmartUnmarshalRaw1[[]testUser](s.db.Query(
`BEGIN;
THROW 'blocked';
COMMIT;
`,
map[string]interface{}{}))

s.Require().Error(err)
s.Require().Contains(err.Error(), "blocked")
})

}

func (s *SurrealDBTestSuite) TestSmartMarshalQuery() {
user := []testUser{{
Username: "electwix",
Expand Down
55 changes: 55 additions & 0 deletions pkg/marshal/marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ type RawQuery[I any] struct {
Detail string `json:"detail"`
}

type RawQueryRaw[I any] struct {
Status string `json:"status"`
Time string `json:"time"`
Result I `json:"result"`
Detail string `json:"detail"`
}

// Unmarshal loads a SurrealDB response into a struct.
func Unmarshal(data, v interface{}) (err error) {
var jsonBytes []byte
Expand Down Expand Up @@ -110,6 +117,54 @@ func SmartUnmarshal[I any](respond interface{}, wrapperError error) (outputs []I
return outputs, err
}

// SmartUnmarshalRaw1 using generics for return desired type.
// Supports only raw queries with 1 returned result.
// example: [map[result:true status:OK time:4.219606ms]]
// handles THROW errors: [map[result:An error occurred: blocked status:ERR time:4.219606ms]]
func SmartUnmarshalRaw1[I any](respond interface{}, wrapperError error) (output I, err error) {
// Handle delete
if respond == nil || wrapperError != nil {
return output, wrapperError
}
data, err := json.Marshal(respond)
if err != nil {
return output, err
}

var rawArr []RawQueryRaw[I]
if err = json.Unmarshal(data, &rawArr); err == nil {
if len(rawArr) == 1 {
raw := rawArr[0]
if raw.Status != StatusOK {
err = errors.Join(err, errors.New(raw.Detail))
} else {
output = raw.Result
}
} else {
err = errors.New("invalid response")
}
} else {
// Error in Result field
unmarshalError := err
var rawError []RawQueryRaw[string]
if err = json.Unmarshal(data, &rawError); err == nil {
if len(rawError) == 1 {
if rawError[0].Status != StatusOK {
err = errors.Join(err, errors.New(rawError[0].Result))
} else {
err = errors.Join(err, unmarshalError)
}
} else {
err = errors.New("invalid response")
}
} else {
err = errors.Join(err, unmarshalError)
}
}

return output, err
}

// Used for define table name, it has no value.
type Basemodel struct{}

Expand Down

0 comments on commit 1d01200

Please sign in to comment.