How to get a struct in graphql resolvers with xorm? - go

Here is the struct in models_gen.go
type Students struct {
StudentID string `json:"student_id"`
Class string `json:"class"`
TestsHistory []*TestsHistory `json:"test_history"
}
type TestsHistory struct {
English string `json:"eng"`
Math string `json:"math"`
Music string `json:"music"`
PE string `json:"pe"`
}
The query function in resolvers.go:
func (r *queryResolver) GetStudents(ctx context.Context, id *int) (*model.Students, error) {
var students []*model.students
engine.Sync2(new(model.students))
fmt.Printf("%v\n", engine.Find(students))
return nil, nil
}
When I check MySQL, the students table only contains "StudentID" and "Class" Field, no "TestsHistory". How do I get "TestsHistory" in resolvers?

Related

Unmarshal to Enum type using a custom Scan func

I'm trying to unmarshall a Gender type that is an Enumeration that is just int64 under the hood (I don't want to use any string representation of this type).
Problem is that the Gender value is not handled properly, I always ended with 0.
I'm missing something but I don't see it...
Thank you so much.
https://go.dev/play/p/bfnI_ESpzJY
package main
import (
"database/sql"
"encoding/json"
"fmt"
)
type Person struct {
name string `json:"name"`
gender Gender `json:"gender"`
}
type Gender int64
const (
Undefined Gender = iota
Male
Female
NonBinary
)
func (g *Gender) Scan(v interface{}) error {
if v == nil {
*g = Gender(Undefined)
return nil
}
ns := sql.NullInt64{}
if err := ns.Scan(v); err != nil {
return err
}
if !ns.Valid {
return fmt.Errorf("Gender.Scan: column is not nullable")
}
if ns.Int64 > 3 {
return fmt.Errorf("Gender.Scan: gender value > 3")
}
*g = genderFromInt64(ns.Int64) // tried Gender(ns.Int64) without success
return nil
}
// tried genderFromInt64(i int64) instead of Gender(ns.Int64) without success
func genderFromInt64(i int64) Gender {
switch i {
case 0:
return Undefined
case 1:
return Male
case 2:
return Female
case 3:
return NonBinary
default:
return Female
}
}
// Value() is not used yet
func (g Gender) Value() (driver.Value, error) {
return int64(g), nil
}
func (g Gender) String() string {
return [...]string{"Undefined", "Male", "Female", "Non-binary"}[g]
}
func main() {
var person Person
jsonPerson := `{"name": "John", "gender": 2}`
json.Unmarshal([]byte(jsonPerson), &person)
fmt.Printf("%+v", person)
}
In your code, I want to point out 2 things (which is already mention by #mkopriva)
Person's fields are unexported and are not visible to unmarshal methods. You can use go vet command it will help you to prevent these kinds of errors
You have implemented the Stringer interface which converts Gender value from enum to string. if you don't want to represent this type as a string you can comment String method
func (g Gender) String() string { return [...]string{"Undefined", "Male", "Female", "Non-binary"}[g] }
https://go.dev/play/p/C1ZUv_fES0k

How to update struct data for array

I am trying to update "age" of data struct using SetAge() function after the array creation in the user struct. Here is the code snippet:
//data struct to set the user details
type data struct {
Name string `json:"name"`
College string `json:"college"`
Age int64 `json:"age"`
}
// user struct to store the user details in JSON Array
type user struct {
DataValue []*data `json:"data"`
}
func (u *user) Details(name, college string) *user {
d:=&data{Name:name, College:college}
u.DataValue=append(u.DataValue, d)
return u
}
func (u *user) SetAge(age int64) *user { //age is optional
// what code should be here such that age is added to resp detail
}
Output:
"data":[{
"name":"test",
"college":"test",
"age":10
},{
"name":"test",
"college":"test"
// in this object "Age" hasn't been set
}]
If you want to update the Age field of all the data objects, You're almost done.
You just need to iterate over the u.DataValue slice, and update the age field as follows:
func (u *user) SetAge(age int64) *user {
for index := range u.DataValue {
u.DataValue[index].Age = age
}
return u
}
As per the requirement in my application, it would be like this:
func (u *user) SetAge(age int64) *user {
u.DataValue[len(u.DataValue) - 1].Age = age
return u
}

Marshal JSON field in database to struct using Pop

I'm using Go Buffalo's ORM Pop and want to store JSON in a field and be able to marshal it into a struct.
e.g.
schema.sql
CREATE TABLE tree (
id uuid PRIMARY KEY,
name text NOT NULL,
fruit json,
);
main.go
type Tree struct {
ID uuid.UUID `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Fruit []Fruit `json:"fruit" db:"fruit"`
}
type Fruit struct {
ID int `json:"id"`
Name string `json:"name"`
}
I get the following error:
sql: converting argument $25 type: unsupported type []Fruit, a slice of struct
UPDATE
Based on feedback added the following methods:
type Fruits []Fruit
// Value implements the driver.Valuer interface
func (f Fruits) Value() (driver.Value, error) {
return json.Marshal(of)
}
// Scan implements the sql.Scanner interface
func (f * Fruits) Scan(value interface{}) error {
var data []byte
if b, ok := value.([]byte); !ok {
data = b
return errors.New("type assertion to []byte for Fruit failed")
}
return json.Unmarshal(data, &of)
}
Now receive the error:
Cannot fetch from database: unable to fetch records: sql: Scan error on column index 26, name "fruit": unexpected end of JSON input
Update 2
Updated Scan method to the following and fixed error:
// Scan implements the sql.Scanner interface
func (f * Fruits) Scan(value interface{}) error {
var data = []byte(value.([]uint8))
return json.Unmarshal(data, &of)
}
Based on the help provided by #mkopriva
You need to provide Value and Scan methods for your struct
// Tree tall tree with many fruit
type Tree struct {
ID uuid.UUID `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Fruit []Fruit `json:"fruit" db:"fruit"`
}
// Fruit fruit found on a tree
type Fruit struct {
ID int `json:"id"`
Name string `json:"name"`
}
// Creates a splice of Fruit
type Fruits []Fruit
// Value implements the driver.Valuer interface
func (f Fruits) Value() (driver.Value, error) {
return json.Marshal(of)
}
// Scan implements the sql.Scanner interface
func (f * Fruits) Scan(value interface{}) error {
var data = []byte(value.([]uint8))
return json.Unmarshal(data, &f)
}

Golang jsonapi requires string or int but mongo needs bson.ObjectId

Playing with go and the following packages:
github.com/julienschmidt/httprouter
github.com/shwoodard/jsonapi
gopkg.in/mgo.v2/bson
I have the following structs:
type Blog struct{
Posts []interface{}
}
type BlogPost struct {
Id bson.ObjectId `jsonapi:"primary,posts" bson:"_id,omitempty"`
Author string `jsonapi:"attr,author"`
CreatedDate time.Time `jsonapi:"attr,created_date"`
Body string `jsonapi:"attr,body"`
Title string `jsonapi:"attr,title"`
}
and this router handler:
func (blog *Blog) GetAll(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if err := jsonapi.MarshalManyPayload(w, blog.Posts); err != nil {
http.Error(w, err.Error(), 500)
}
}
When the handler function is called it spits out the error:
id should be either string or int
How should this struct look so I can use it with mgo and jsonapi?
Create one more struct of Blog like below
type BlogPostVO struct {
Id string `jsonapi:"primary,posts" bson:"_id,omitempty"`
Author string `jsonapi:"attr,author"`
CreatedDate time.Time `jsonapi:"attr,created_date"`
Body string `jsonapi:"attr,body"`
Title string `jsonapi:"attr,title"`
}
and use the below function in your controller to parse
func parseToVO(blog *models.Blog) *models.BlogVO {
bolgVO := models.BlogVO{}
bolgVO.Id = blog.Id.Hex()
bolgVO.Author = blog.Author
bolgVO.CreatedDate = blog.CreatedDate
bolgVO.Body = blog.Body
bolgVO.Title = blog.Title
return &models.Blog
}
this worked for me

Unmarshal dynamic XML

I have been using unmarshal without any problems until I came across a situation where the XML tag name is dynamic.
XML Could look like:
<unit_amount_in_cents>
<USD type="integer">4000</USD>
</unit_amount_in_cents>
<setup_fee_in_cents>
<USD type="integer">4000</USD>
</setup_fee_in_cents>
or
<unit_amount_in_cents>
<GBP type="integer">4000</GBP>
</unit_amount_in_cents>
<setup_fee_in_cents>
<GBP type="integer">4000</GBP>
</setup_fee_in_cents>
or could have both (or more)
<unit_amount_in_cents>
<USD type="integer">4000</USD>
<GBP type="integer">4000</GBP>
</unit_amount_in_cents>
<setup_fee_in_cents>
<USD type="integer">4000</USD>
<GBP type="integer">4000</USD>
</setup_fee_in_cents>
I can marshal to xml w/o problems by assigning the XML.Name.Local to what I need it to be but can't unmarshal it.
Here is what the struct looks like
type Plan struct {
XMLName xml.Name `xml:"plan"`
Name string `xml:"name,omitempty"`
PlanCode string `xml:"plan_code,omitempty"`
Description string `xml:"description,omitempty"`
SuccessUrl string `xml:"success_url,omitempty"`
CancelUrl string `xml:"cancel_url,omitempty"`
DisplayDonationAmounts bool `xml:"display_donation_amounts,omitempty"`
DisplayQuantity bool `xml:"display_quantity,omitempty"`
DisplayPhoneNumber bool `xml:"display_phone_number,omitempty"`
BypassHostedConfirmation bool `xml:"bypass_hosted_confirmation,omitempty"`
UnitName string `xml:"unit_name,omitempty"`
PaymentPageTOSLink string `xml:"payment_page_tos_link,omitempty"`
PlanIntervalLength int `xml:"plan_interval_length,omitempty"`
PlanIntervalUnit string `xml:"plan_interval_unit,omitempty"`
AccountingCode string `xml:"accounting_code,omitempty"`
CreatedAt *time.Time `xml:"created_at,omitempty"`
SetupFeeInCents CurrencyArray `xml:"setup_fee_in_cents,omitempty"`
UnitAmountInCents CurrencyArray `xml:"unit_amount_in_cents,omitempty"`
}
type CurrencyArray struct {
CurrencyList []Currency
}
func (c *CurrencyArray) AddCurrency(currency string, amount int) {
newc := Currency{Amount:fmt.Sprintf("%v",amount)}
newc.XMLName.Local = currency
c.CurrencyList = append(c.CurrencyList, newc)
}
func (c *CurrencyArray) GetCurrencyValue(currency string) (value int, e error) {
for _, v := range c.CurrencyList {
if v.XMLName.Local == currency {
value, e = strconv.Atoi(v.Amount)
return
}
}
e = errors.New(fmt.Sprintf("%s not found",currency))
return
}
type Currency struct {
XMLName xml.Name `xml:""`
Amount string `xml:",chardata"`
}
You need the tag xml:",any" on your CurrencyList field.
http://play.golang.org/p/i23w03z6R4

Resources