Skip to content

Commit

Permalink
Added example and fixed bug
Browse files Browse the repository at this point in the history
  • Loading branch information
Salman Ahmad committed Apr 3, 2018
1 parent 7c3c153 commit bded7d4
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 1 deletion.
9 changes: 9 additions & 0 deletions example/social/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
### Social App

A simple example of how to use struct fields as resolvers instead of methods.

To run this server

`go ./example/field-resolvers/server/server.go`

and go to localhost:9011 to interact
66 changes: 66 additions & 0 deletions example/social/server/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package main

import (
"log"
"net/http"

"github.com/graph-gophers/graphql-go"
"github.com/graph-gophers/graphql-go/config"
"github.com/graph-gophers/graphql-go/example/social"
"github.com/graph-gophers/graphql-go/relay"
)

var schema *graphql.Schema

func init() {
schema = graphql.MustParseSchema(social.Schema, &social.Resolver{}, &config.Config{UseResolverMethods: false})
}

func main() {
http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write(page)
}))

http.Handle("/query", &relay.Handler{Schema: schema})

log.Fatal(http.ListenAndServe(":9011", nil))
}

var page = []byte(`
<!DOCTYPE html>
<html>
<head>
<link href="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.11.11/graphiql.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-promise/4.1.1/es6-promise.auto.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.3/fetch.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.11.11/graphiql.min.js"></script>
</head>
<body style="width: 100%; height: 100%; margin: 0; overflow: hidden;">
<div id="graphiql" style="height: 100vh;">Loading...</div>
<script>
function graphQLFetcher(graphQLParams) {
return fetch("/query", {
method: "post",
body: JSON.stringify(graphQLParams),
credentials: "include",
}).then(function (response) {
return response.text();
}).then(function (responseBody) {
try {
return JSON.parse(responseBody);
} catch (error) {
return responseBody;
}
});
}
ReactDOM.render(
React.createElement(GraphiQL, {fetcher: graphQLFetcher}),
document.getElementById("graphiql")
);
</script>
</body>
</html>
`)
170 changes: 170 additions & 0 deletions example/social/social.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package social

import (
"context"
"errors"
"fmt"
)

const Schema = `
schema {
query: Query
}
type Query {
admin(id: ID!, role: Role = ADMIN): Admin!
user(id: ID!): User!
}
interface Admin {
id: ID!
name: String!
role: Role!
}
type User implements Admin {
id: ID!
name: String!
email: String!
role: Role!
phone: String!
address: [String!]
friends(page: Pagination): [User]
}
input Pagination {
first: Int
last: Int
}
enum Role {
ADMIN
USER
}
`

type page struct {
First *int
Last *int
}

type admin interface {
IdResolver() string
NameResolver() string
RoleResolver() string
}

type user struct {
Id string
Name string
Role string
Email string
Phone string
Address *[]string
Friends *[]*user
}

func (u user) IdResolver() string {
return u.Id
}

func (u user) NameResolver() string {
return u.Name
}

func (u user) RoleResolver() string {
return u.Role
}

func (u user) FriendsResolver(args struct{ Page *page }) (*[]*user, error) {

from := 0
numFriends := len(*u.Friends)
to := numFriends

if args.Page != nil {
if args.Page.First != nil {
from = *args.Page.First
}
if args.Page.Last != nil {
to = *args.Page.Last
if to > numFriends {
to = numFriends
}
}
}

friends := (*u.Friends)[from:to]

return &friends, nil
}

var users = []*user{
{
Id: "0x01",
Name: "Albus Dumbledore",
Role: "ADMIN",
Email: "[email protected]",
Phone: "000-000-0000",
Address: &[]string{"Office @ Hogwarts", "where Horcruxes are"},
},
{
Id: "0x02",
Name: "Harry Potter",
Role: "USER",
Email: "[email protected]",
Phone: "000-000-0001",
Address: &[]string{"123 dorm room @ Hogwarts", "456 random place"},
},
{
Id: "0x03",
Name: "Hermione Granger",
Role: "USER",
Email: "[email protected]",
Phone: "000-000-0011",
Address: &[]string{"233 dorm room @ Hogwarts", "786 @ random place"},
},
{
Id: "0x04",
Name: "Ronald Weasley",
Role: "USER",
Email: "[email protected]",
Phone: "000-000-0111",
Address: &[]string{"411 dorm room @ Hogwarts", "981 @ random place"},
},
}

var usersMap = make(map[string]*user)

func init() {
users[0].Friends = &[]*user{users[1]}
users[1].Friends = &[]*user{users[0], users[2], users[3]}
users[2].Friends = &[]*user{users[1], users[3]}
users[3].Friends = &[]*user{users[1], users[2]}
for _, usr := range users {
usersMap[usr.Id] = usr
}
}

type Resolver struct{}

func (r *Resolver) Admin(ctx context.Context, args struct {
Id string
Role string
}) (admin, error) {
if usr, ok := usersMap[args.Id]; ok {
if usr.Role == args.Role {
return *usr, nil
}
}
err := fmt.Sprintf("user with id=%s and role=%s does not exist", args.Id, args.Role)
return user{}, errors.New(err)
}

func (r *Resolver) User(ctx context.Context, args struct{ Id string }) (user, error) {
if usr, ok := usersMap[args.Id]; ok {
return *usr, nil
}
err := fmt.Sprintf("user with id=%s does not exist", args.Id)
return user{}, errors.New(err)
}
7 changes: 6 additions & 1 deletion internal/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,12 @@ func execFieldSelection(ctx context.Context, r *Request, f *fieldToExec, path *p
return err
}
} else {
result = f.resolver.Field(f.field.FieldIndex)
// TODO extract out unwrapping ptr logic to a common place
res := f.resolver
if res.Kind() == reflect.Ptr {
res = res.Elem()
}
result = res.Field(f.field.FieldIndex)
}

return nil
Expand Down

0 comments on commit bded7d4

Please sign in to comment.