I am using the graphql-go library along with graphql-go/relay. I am trying to write some basic tests for my API, and am having some trouble with the base64 encoding mechanism. When I execute a simple query for viewer id, the viewer encoded id is wrong.
I am hardcoding the db call to retrieve the first user with id 1:
func TestGetViewer(t *testing.T) {
// Empty and fill the db for consistency
prepDb()
query := `
query {
viewer {
id
}
}
`
// The schema contains a viewer entry point that returns a userType:
// userType = graphql.NewObject(graphql.ObjectConfig{
// Name: "User",
// Description: "A user",
// Fields: graphql.Fields{
// "id": relay.GlobalIDField("User", nil),
// },
// })
schema := gql.NewSchema(db)
// graphql.Do is called by graphql.Execute. I'm trying to get
// closer to what is actually called in order to debug
result := graphql.Do(graphql.Params{
Schema: schema,
RequestString: query,
})
// The return data from our query
data := result.Data.(map[string]interface{})
// The relay.GlobalIDField in our schema should be calling this method
// under the hood, however, as you'll see it returns something different
id := relay.ToGlobalID("User", "1")
// prints map[viewer:map[id:VXNlcjo= username:janedoe]]
// notice the last character of the id
fmt.Println(data)
// prints VXNlcjox
fmt.Println(id)
The graphql.Do returns something that is really close, but the last character of the encoded id is different from what is returned by relay.ToGlobalID. It instead shows the last character padded with =. And when I try to run relay.FromGlobalID("VXNlcjo="), it is able to figure out that its type is User, but it returns an empty string for ID. Whereas, if I replace the = with x, it returns the correct object.
I'm new to Go, and to these concepts, so any ideas or help would be greatly appreciated!
I'm a big ol' dummy and neglected to write the json field tags when defining my User struct to retrieve data from the db. Single capitalization is okay, but since I capitalize both I and D, it wasn't resolving properly and was passing an empty string for the id. Below is corrected:
type User struct {
ID int `json:"id"`
}
Rookie mistake! But maybe it can be helpful to others.
Related
I have a couple handlers in a web app (Fiber framework) where one handler retrieves data from an external API and the other handler takes a subset of this data and performs some business logic (ie sends a report, etc).
Both handlers are in the same package. In handler2.go I am able to dereference the data from handler1.go and I want to use specific values from that data to populate the struct fields in handler2.go. The dereferenced data from handler1.go is itself an array of structs that I can loop over.
In handler2.go , I have a struct:
type Report struct {
contact string
date string
resource string
}
// get data from handler1.go function and use it to populate the Report struct
// each "report" is a struct, so need to create a list of structs
func getReportData() {
reportData := GetReport() // call function in handler1.go
for _, report := range *reportData {
fmt.Println(report.Date)
}
So instead of simply printing the data (the print statement is just to show that I have access to the data I need) I want to populate the Report struct with specifc items from the data that I can can access using the loop and the report.<KEY> syntax.
How can I create a list of structs (using the Report struct) populated with the data I can get via this for loop?
For an MVP , I can simply format this list of structs (in json) and display an endpoint in the web app. I am just struggling with how to construct this data properly.
To answer the direct question, if we assume that the values returned by GetReport() have Date, Contact, and Resource fields, then you could write:
type Report struct {
contact string
date string
resource string
}
// Return a list (well, slice) of Reports
func getReportData() (reports []Report) {
reportData := GetReport()
for _, report := range reportData {
myReport := Report{
contact: report.Contact,
date: report.Date,
resource: report.Resource,
}
reports = append(reports, myReport)
}
return
}
I am writing a backend API that sends to the frontend some JSON data from a database (handled by GORM). The frontend, after some modifications to the content of the fields, sends me back the data also as JSON.
Below is a mock-up of the code
package main
import (
"encoding/json"
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type User struct {
ID uint
Name string
Card Card
CardID uint
}
type Card struct {
ID uint
Number string
}
func main() {
// initialize the database
db, _ := gorm.Open(sqlite.Open("mytest.sqlite"), &gorm.Config{})
db.Debug()
db.AutoMigrate(&User{}, &Card{})
// create two cards
db.Create(&Card{
Number: "42",
})
db.Create(&Card{
Number: "100",
})
// find one of them
var myCard Card
db.Where(map[string]interface{}{"number": "42"}).First(&myCard)
// create a user with that card
db.Create(&User{
Name: "john",
Card: myCard,
})
// find that user
var myUser User
db.Preload("Card").Where(map[string]interface{}{"name": "john"}).Last(&myUser)
// this is then marshalled to JSON, and sent to a frontend.
// on that frontend there are some changes and I get back the JSON
// this is simulated below:
// the card itself changed
myUser.Card.ID = "2"
myUser.Card.ID = "100"
// the name of the user changed
myUser.Name = "mary"
output, _ := json.Marshal(myUser)
fmt.Print(string(output))
// Now output holds the JSON string
}
// output
// {"ID":12,"Name":"mary","Card":{"ID":2,"Number":"100"},"CardID":1}
My question is about how to handle output so that it can be written back into the database.
I do not really know GORM (or ORM for that matter) and I do not understand how to handle the fact that:
the ID of the card was changed
the Name of the user changed
In particular, I do not understand whether GORM will just "know" that the attached card is now different (and corresponds to a card in the database). I believe that the fact that Name changed is not a problem (because the ID is the same)
You can decode the JSON body into a user struct, and then do an update with it.
func patchUser(w http.ResponseWriter, r *http.Request) {
// get the id from the url i.e. /users/1
// you need to use a framework or implement this yourself
id := getId(r.URL)
// decode the body into a new user
patch := User{}
json.NewDecoder(r.Body).Decode(&patch)
// patch the user at the given id
db.Model(User{}).Where("id = ?", id).Updates(patch)
}
You can find docs about updates here: https://gorm.io/docs/update.html
I'm building a graphql interface using golang. I'm using gqlgen package to implement it.
Here I need to pass all field names in a query to get it in response, But the problem is my data is huge, it is having more than 30 fields it would be difficult to pass all fields in a query.
This is my query
{Model{id, name, email, mobile,...............}}
Like this I need to pass all fields name.
Instead Im looking for a result which will return all fields without passing any fields. I mean if not passing any field names it should return all.
For example
{Model{}}
First, you really should list out all the fields in your query. That is the nature of graphql. It is verbose, but most client libraries get the fields from your data structure anyway, so it's not that bad.
So I recommend listing out all fields manually!
Using Scalars (must be on v0.11.3 or below, see https://github.com/99designs/gqlgen/issues/1293)
But if you insist, if there is a will, there is way. You can use GraphQL's scalar types and make your own. See this doc for how to make them with gqlgen: https://gqlgen.com/reference/scalars/
In your schema, you can make a JSON scalar:
scalar JSON
type Query {
random: JSON!
}
Make a model for this
// in your own models.go
// You can really play with this to make it better, easier to use
type JSONScalar json.RawMessage
// UnmarshalGQL implements the graphql.Unmarshaler interface
func (y *JSONScalar) UnmarshalGQL(v interface{}) error {
data, ok := v.(string)
if !ok {
return fmt.Errorf("Scalar must be a string")
}
*y = []byte(data)
return nil
}
// MarshalGQL implements the graphql.Marshaler interface
func (y JSONScalar) MarshalGQL(w io.Writer) {
_, _ = w.Write(y)
}
Then link the scalar to your custom type in the gql.yml
models:
JSON:
model: github.com/your-project/path/graph/model.JSONScalar
When you run the generate (use gqlgen v0.11.3 or below, gqlgen version), your resolvers will now use the custom type you made. And it's easy to use:
func (r *queryResolver) random(ctx context.Context) (model.JSONScalar, error) {
// something is the data structure you want to return as json
something := struct {
Value string
}{
Value: "Hello World",
}
d, _ := json.Marshal(something)
return model1.JSONScalar(d), nil
}
The resulting query of
// Query
{
random
}
// Response
{
"random" : {
"Value": "Hello World!"
}
}
This issue is migrated from a question on our Github account because we want the answer to be available to others. Here is the original question:
Hello,
Following is the InstanceQuery I tried
http://localhost:3000/3_0_1/Questionnaire/jamana/$graphql?query={id}
I am receiving back response as Cannot query field \"id\" on type \"Questionnaire_Query\"
So what is the right format I should try ?
https://build.fhir.org/graphql.html has a sample as http://test.fhir.org/r3/Patient/example/$graphql?query={name{text,given,family}}.Its working in their server. I cannot get the response When I try similarly in our graphql-fhir.
Original answer from Github:
We are using named queries since we are using express-graphql. I do not believe that is valid syntax. Also, the url provided does not seem to work, I just get an OperationOutcome saying the patient does not exist, which is not a valid GraphQL response.
Can you try changing your query from:
http://localhost:3000/3_0_1/Questionnaire/jamana/$graphql?query={id}
to this:
http://localhost:3000/3_0_1/Questionnaire/jamana/$graphql?query={Questionnaire{id}}
When writing the query, you need to provide the return type as part of the instance query. You should get a response that looks like similar to this(if you have implemented your resolver you will have data and not null):
{
"data": {
"Questionnaire": {
"id": null
}
}
}
and from a later comment:
If you are getting null then you are doing it correctly, but you haven't wrote a query or connected it to a data source. You still need to return the questionnaire in the resolver.
Where you are seeing this:
instance: {
name: 'Questionnaire',
path: '/3_0_1/Questionnaire/:id',
query: QuestionnaireInstanceQuery,
},
You are seeing the endpoint being registered with an id parameter, which is different from a GraphQL argument. This is just an express argument. If you navigate to the questionnaire/query.js file, you can see that the QuestionnaireInstanceQuery query has a different resolver than the standard QuestionnaireQuery. So in your questionnaire/resolver.js file, if you want both query and instance query to work, you need to implement both resolvers.
e.g.
// This is for the standard query
module.exports.getQuestionnaire = function getQuestionnaire(
root,
args,
context = {},
info,
) {
let { server, version, req, res } = context;
// Do query and return questionnaire
return {};
};
// This one is for a questionnaire instance
module.exports.getQuestionnaireInstance = function getQuestionnaireInstance(
root,
args,
context = {},
info,
) {
let { server, version, req, res } = context;
// req.params.id is your questionnaire id, use that for your query here
// queryQuestionnaireById does not exist, it is pseudo code
// you need to query your database here with the id
let questionnaire = queryQuestionnaireById(req.params.id);
// return the correct questionnaire here, default returns {},
// which is why you see null, because no data is returned
return questionnaire;
};
I have 2 structs with data like this:
type User struct {
Pics Pic[]
}
type Pic struct {
Id int
UserId int64
}
Although everytime I insert an User, Each of the pics are inserted on their table everytime I find the users, pics are not populated:
var users []User
db.Limit(pagesize).Where("updated_at > ?", date).Find(&users)
Am I doing something wrong?
Your models (the structs) don't really make sense because User have a Pic array indicates a 'one to many' user to pics relationship however your user has no id property itself and there for cannot be related to items on the Pic table.
User should have a property Id which will be it's primary key and UserId is a foreign key on Pic that relates to it. Without the 'relation' between these two tables/entities there's no way you're going to return pics by querying users.
I'm not sure what all you need to do to make your code work since the example is incomplete but the first thing you need is an Id property which you should designate as a Primarykey with gorm annotations. You also should have annotations on the Pic struct saying UserId is a foreign key and Id is it's primary key.
Also, just fyi your array is not embedded. Embedding is a language feature which you're not using, if you embed the property it has no name and it's properties can be accessed directly from an instance of the embedding type.
I had these issues once. Then I used Join function.
See my example that works just fine:
type FileType struct {
Id int
}
type File struct {
Id int
FileType `xorm:"extends"`
}
file := File{Id: id}
has, err := eng.
Join("INNER", "FileType", "FileType.IdFileType = File.IdFileType").
Get(&file)
You probably know by now. You got to think as if you are creating a SQL table with 1-to-many relationship. Here is an example:
type Entry struct {
ID int
Name string
...
ContainerID int
}
type Container struct {
ID int
Tag int
Name string
...
Entries []Entry `gorm:"foreignkey:ContainerID"`
}
The trick is to populate it. I am yet to find how to make it in one try. For every such dependency, you got to run something like:
c := getContainerFromDB(...)
if err := getROConn().Model(c).Related(&c.Entries, "Entries").Error; err != nil {
return errors.Wrap(err, "error getting container field")
}
Try Preload
db.Limit(pagesize).Where("updated_at > ?", date).Preload("Pics").Find(&users)