func NewAPIs() (*apis, error) { apis := &apis{} apis.nodeDefinitions = relay.NewNodeDefinitions(relay.NodeDefinitionsConfig{ IDFetcher: func(id string, info graphql.ResolveInfo, ctx context.Context) (interface{}, error) { resolvedID := relay.FromGlobalID(id) switch resolvedID.Type { case "Search": return apis.SearchService.ByClientID(ctx, resolvedID.ID) case "Space": return apis.SpaceService.ByID(ctx, resolvedID.ID) case "User": return apis.UserService.ByID(ctx, resolvedID.ID) default: return nil, fmt.Errorf("Unknown node type %s", resolvedID.Type) } }, TypeResolve: func(p graphql.ResolveTypeParams) *graphql.Object { switch p.Value.(type) { case *Search: return apis.SearchAPI.Type case *Space: return apis.SpaceAPI.Type case *Task: return apis.TaskAPI.Type case *User: return apis.UserAPI.Type } return nil }, }) graph := &inject.Graph{} err := graph.Provide( &inject.Object{ Value: apis, }, &inject.Object{ Value: apis.nodeDefinitions.NodeInterface, Name: "node", }, ) if err != nil { return nil, err } err = graph.Populate() if err != nil { return nil, err } err = startstop.Start(graph.Objects(), nil) if err != nil { return nil, err } return apis, nil }
func (api *UserAPI) Start() error { api.Type = graphql.NewObject(graphql.ObjectConfig{ Name: "User", Description: "User represents a person who can interact with the app.", Fields: graphql.Fields{ "id": relay.GlobalIDField("User", nil), "isAdmin": &graphql.Field{ Description: "Whether or not the user is an Ellie's Pad admin.", Type: graphql.Boolean, }, "email": &graphql.Field{ Description: "The user's email primary address.", Type: graphql.String, }, "name": &graphql.Field{ Description: "The user's full name.", Type: graphql.String, }, "givenName": &graphql.Field{ Description: "The user's given name.", Type: graphql.String, }, "familyName": &graphql.Field{ Description: "The user's family name.", Type: graphql.String, }, "imageUrl": &graphql.Field{ Description: "The user's profile picture URL.", Type: graphql.String, }, "space": &graphql.Field{ Args: graphql.FieldConfigArgument{ "id": &graphql.ArgumentConfig{ Type: graphql.String, DefaultValue: "", Description: "id can be omitted, which will have space resolve to the user's default space.", }, }, Description: "space is a disjoint universe of views, searches and tasks.", Type: api.SpaceAPI.Type, Resolve: func(p graphql.ResolveParams) (interface{}, error) { span := trace.FromContext(p.Context).NewChild("trythings.userAPI.space") defer span.Finish() id, ok := p.Args["id"].(string) if ok { resolvedID := relay.FromGlobalID(id) if resolvedID == nil { return nil, fmt.Errorf("invalid id %q", id) } sp, err := api.SpaceService.ByID(p.Context, resolvedID.ID) if err != nil { return nil, err } return sp, nil } u, ok := p.Source.(*User) if !ok { return nil, errors.New("expected user source") } sps, err := api.SpaceService.ByUser(p.Context, u) if err != nil { return nil, err } if len(sps) == 0 { return nil, errors.New("could not find default space for user") } return sps[0], nil }, }, "spaces": &graphql.Field{ Type: graphql.NewList(api.SpaceAPI.Type), Resolve: func(p graphql.ResolveParams) (interface{}, error) { span := trace.FromContext(p.Context).NewChild("trythings.userAPI.spaces") defer span.Finish() u, ok := p.Source.(*User) if !ok { return nil, errors.New("expected user source") } sps, err := api.SpaceService.ByUser(p.Context, u) if err != nil { return nil, err } return sps, nil }, }, }, Interfaces: []*graphql.Interface{ api.NodeInterface, }, }) return nil }
func (api *TaskAPI) Start() error { api.Type = graphql.NewObject(graphql.ObjectConfig{ Name: "Task", Description: "Task represents a particular action or piece of work to be completed.", Fields: graphql.Fields{ "id": relay.GlobalIDField("Task", nil), "createdAt": &graphql.Field{ Description: "When the task was first added", Type: graphql.String, }, "title": &graphql.Field{ Description: "A short summary of the task", Type: graphql.String, }, "description": &graphql.Field{ Description: "A more detailed explanation of the task", Type: graphql.String, }, "isArchived": &graphql.Field{ Description: "Whether this task requires attention", Type: graphql.Boolean, }, }, Interfaces: []*graphql.Interface{ api.NodeInterface, }, }) api.ConnectionType = relay.ConnectionDefinitions(relay.ConnectionConfig{ Name: api.Type.Name(), NodeType: api.Type, }).ConnectionType api.Mutations = map[string]*graphql.Field{ "addTask": relay.MutationWithClientMutationID(relay.MutationConfig{ Name: "AddTask", InputFields: graphql.InputObjectConfigFieldMap{ "title": &graphql.InputObjectFieldConfig{ Type: graphql.NewNonNull(graphql.String), }, "description": &graphql.InputObjectFieldConfig{ Type: graphql.String, }, "spaceId": &graphql.InputObjectFieldConfig{ Type: graphql.NewNonNull(graphql.String), }, }, OutputFields: graphql.Fields{ "task": &graphql.Field{ Type: api.Type, Resolve: func(p graphql.ResolveParams) (interface{}, error) { payload, ok := p.Source.(map[string]interface{}) if !ok { return nil, errors.New("could not cast payload to map") } id, ok := payload["taskId"].(string) if !ok { return nil, errors.New("could not cast taskId to string") } t, err := api.TaskService.ByID(p.Context, id) if err != nil { return nil, err } return t, nil }, }, }, MutateAndGetPayload: func(inputMap map[string]interface{}, info graphql.ResolveInfo, ctx context.Context) (map[string]interface{}, error) { title, ok := inputMap["title"].(string) if !ok { return nil, errors.New("could not cast title to string") } var desc string descOrNil := inputMap["description"] if descOrNil != nil { desc, ok = descOrNil.(string) if !ok { return nil, errors.New("could not cast description to string") } } spaceID, ok := inputMap["spaceId"].(string) if !ok { return nil, errors.New("could not cast spaceId to string") } resolvedSpaceID := relay.FromGlobalID(spaceID) if resolvedSpaceID == nil { return nil, fmt.Errorf("invalid id %q", spaceID) } t := &Task{ Title: title, Description: desc, SpaceID: resolvedSpaceID.ID, } err := api.TaskService.Create(ctx, t) if err != nil { return nil, err } return map[string]interface{}{ "taskId": t.ID, }, nil }, }), "editTask": relay.MutationWithClientMutationID(relay.MutationConfig{ Name: "EditTask", InputFields: graphql.InputObjectConfigFieldMap{ "id": &graphql.InputObjectFieldConfig{ Type: graphql.NewNonNull(graphql.ID), }, "title": &graphql.InputObjectFieldConfig{ Type: graphql.String, }, "description": &graphql.InputObjectFieldConfig{ Type: graphql.String, }, "isArchived": &graphql.InputObjectFieldConfig{ Type: graphql.Boolean, }, }, OutputFields: graphql.Fields{ "task": &graphql.Field{ Type: api.Type, Resolve: func(p graphql.ResolveParams) (interface{}, error) { payload, ok := p.Source.(map[string]interface{}) if !ok { return nil, errors.New("could not cast payload to map") } id, ok := payload["id"].(string) if !ok { return nil, errors.New("could not cast id to string") } t, err := api.TaskService.ByID(p.Context, id) if err != nil { return nil, err } return t, nil }, }, }, MutateAndGetPayload: func(inputMap map[string]interface{}, info graphql.ResolveInfo, ctx context.Context) (map[string]interface{}, error) { id, ok := inputMap["id"].(string) if !ok { return nil, errors.New("could not cast id to string") } resolvedID := relay.FromGlobalID(id) if resolvedID == nil { return nil, fmt.Errorf("invalid id %q", id) } t, err := api.TaskService.ByID(ctx, resolvedID.ID) if err != nil { return nil, err } title, ok := inputMap["title"].(string) if ok { t.Title = title } description, ok := inputMap["description"].(string) if ok { t.Description = description } isArchived, ok := inputMap["isArchived"].(bool) if ok { t.IsArchived = isArchived } err = api.TaskService.Update(ctx, t) if err != nil { return nil, err } return map[string]interface{}{ "id": t.ID, }, nil }, }), } return nil }
"1": &user{1, "John Doe"}, "2": &user{2, "Jane Smith"}, } var globalIDTestPhotoData = map[string]*photo2{ "1": &photo2{1, 300}, "2": &photo2{2, 400}, } // declare types first, define later in init() // because they all depend on nodeTestDef var globalIDTestUserType *graphql.Object var globalIDTestPhotoType *graphql.Object var globalIDTestDef = relay.NewNodeDefinitions(relay.NodeDefinitionsConfig{ IDFetcher: func(globalID string, info graphql.ResolveInfo) interface{} { resolvedGlobalID := relay.FromGlobalID(globalID) if resolvedGlobalID == nil { return nil } if resolvedGlobalID.Type == "User" { return globalIDTestUserData[resolvedGlobalID.ID] } else { return globalIDTestPhotoData[resolvedGlobalID.ID] } }, TypeResolve: func(value interface{}, info graphql.ResolveInfo) *graphql.Object { switch value.(type) { case *user: return globalIDTestUserType case *photo2: return globalIDTestPhotoType
func init() { /** * We get the node interface and field from the relay library. * * The first method is the way we resolve an ID to its object. The second is the * way we resolve an object that implements node to its type. */ nodeDefinitions = relay.NewNodeDefinitions(relay.NodeDefinitionsConfig{ IDFetcher: func(id string, info graphql.ResolveInfo) interface{} { // resolve id from global id resolvedID := relay.FromGlobalID(id) // based on id and its type, return the object if resolvedID.Type == "Faction" { return GetFaction(resolvedID.ID) } else { return GetShip(resolvedID.ID) } }, TypeResolve: func(value interface{}, info graphql.ResolveInfo) *graphql.Object { // based on the type of the value, return GraphQLObjectType switch value.(type) { case *Faction: return factionType default: return shipType } }, }) /** * We define our basic ship type. * * This implements the following type system shorthand: * type Ship : Node { * id: String! * name: String * } */ shipType = graphql.NewObject(graphql.ObjectConfig{ Name: "Ship", Description: "A ship in the Star Wars saga", Fields: graphql.Fields{ "id": relay.GlobalIDField("Ship", nil), "name": &graphql.Field{ Type: graphql.String, Description: "The name of the ship.", }, }, Interfaces: []*graphql.Interface{ nodeDefinitions.NodeInterface, }, }) /** * We define a connection between a faction and its ships. * * connectionType implements the following type system shorthand: * type ShipConnection { * edges: [ShipEdge] * pageInfo: PageInfo! * } * * connectionType has an edges field - a list of edgeTypes that implement the * following type system shorthand: * type ShipEdge { * cursor: String! * node: Ship * } */ shipConnectionDefinition := relay.ConnectionDefinitions(relay.ConnectionConfig{ Name: "Ship", NodeType: shipType, }) /** * We define our faction type, which implements the node interface. * * This implements the following type system shorthand: * type Faction : Node { * id: String! * name: String * ships: ShipConnection * } */ factionType = graphql.NewObject(graphql.ObjectConfig{ Name: "Faction", Description: "A faction in the Star Wars saga", Fields: graphql.Fields{ "id": relay.GlobalIDField("Faction", nil), "name": &graphql.Field{ Type: graphql.String, Description: "The name of the faction.", }, "ships": &graphql.Field{ Type: shipConnectionDefinition.ConnectionType, Args: relay.ConnectionArgs, Resolve: func(p graphql.ResolveParams) interface{} { // convert args map[string]interface into ConnectionArguments args := relay.NewConnectionArguments(p.Args) // get ship objects from current faction ships := []interface{}{} if faction, ok := p.Source.(*Faction); ok { for _, shipId := range faction.Ships { ships = append(ships, GetShip(shipId)) } } // let relay library figure out the result, given // - the list of ships for this faction // - and the filter arguments (i.e. first, last, after, before) return relay.ConnectionFromArray(ships, args) }, }, }, Interfaces: []*graphql.Interface{ nodeDefinitions.NodeInterface, }, }) /** * This is the type that will be the root of our query, and the * entry point into our schema. * * This implements the following type system shorthand: * type Query { * rebels: Faction * empire: Faction * node(id: String!): Node * } */ queryType := graphql.NewObject(graphql.ObjectConfig{ Name: "Query", Fields: graphql.Fields{ "rebels": &graphql.Field{ Type: factionType, Resolve: func(p graphql.ResolveParams) interface{} { return GetRebels() }, }, "empire": &graphql.Field{ Type: factionType, Resolve: func(p graphql.ResolveParams) interface{} { return GetEmpire() }, }, "node": nodeDefinitions.NodeField, }, }) /** * This will return a GraphQLField for our ship * mutation. * * It creates these two types implicitly: * input IntroduceShipInput { * clientMutationID: string! * shipName: string! * factionId: ID! * } * * input IntroduceShipPayload { * clientMutationID: string! * ship: Ship * faction: Faction * } */ shipMutation := relay.MutationWithClientMutationID(relay.MutationConfig{ Name: "IntroduceShip", InputFields: graphql.InputObjectConfigFieldMap{ "shipName": &graphql.InputObjectFieldConfig{ Type: graphql.NewNonNull(graphql.String), }, "factionId": &graphql.InputObjectFieldConfig{ Type: graphql.NewNonNull(graphql.ID), }, }, OutputFields: graphql.Fields{ "ship": &graphql.Field{ Type: shipType, Resolve: func(p graphql.ResolveParams) interface{} { if payload, ok := p.Source.(map[string]interface{}); ok { return GetShip(payload["shipId"].(string)) } return nil }, }, "faction": &graphql.Field{ Type: factionType, Resolve: func(p graphql.ResolveParams) interface{} { if payload, ok := p.Source.(map[string]interface{}); ok { return GetFaction(payload["factionId"].(string)) } return nil }, }, }, MutateAndGetPayload: func(inputMap map[string]interface{}, info graphql.ResolveInfo) map[string]interface{} { // `inputMap` is a map with keys/fields as specified in `InputFields` // Note, that these fields were specified as non-nullables, so we can assume that it exists. shipName := inputMap["shipName"].(string) factionId := inputMap["factionId"].(string) // This mutation involves us creating (introducing) a new ship newShip := CreateShip(shipName, factionId) // return payload return map[string]interface{}{ "shipId": newShip.ID, "factionId": factionId, } }, }) /** * This is the type that will be the root of our mutations, and the * entry point into performing writes in our schema. * * This implements the following type system shorthand: * type Mutation { * introduceShip(input IntroduceShipInput!): IntroduceShipPayload * } */ mutationType := graphql.NewObject(graphql.ObjectConfig{ Name: "Mutation", Fields: graphql.Fields{ "introduceShip": shipMutation, }, }) /** * Finally, we construct our schema (whose starting query type is the query * type we defined above) and export it. */ var err error Schema, err = graphql.NewSchema(graphql.SchemaConfig{ Query: queryType, Mutation: mutationType, }) if err != nil { // panic if there is an error in schema panic(err) } }
func (api *SpaceAPI) Start() error { api.Type = graphql.NewObject(graphql.ObjectConfig{ Name: "Space", Description: "Space represents an access-controlled universe of tasks.", Fields: graphql.Fields{ "id": relay.GlobalIDField("Space", nil), "createdAt": &graphql.Field{ Description: "When the space was first created.", Type: graphql.String, }, "name": &graphql.Field{ Description: "The name to display for the space.", Type: graphql.String, }, "savedSearch": &graphql.Field{ Args: graphql.FieldConfigArgument{ "id": &graphql.ArgumentConfig{ Type: graphql.String, }, }, Type: api.SearchAPI.Type, Resolve: func(p graphql.ResolveParams) (interface{}, error) { span := trace.FromContext(p.Context).NewChild("trythings.spaceAPI.savedSearch") defer span.Finish() id, ok := p.Args["id"].(string) if !ok { return nil, errors.New("id is required") } resolvedID := relay.FromGlobalID(id) if resolvedID == nil { return nil, fmt.Errorf("invalid id %q", id) } se, err := api.SearchService.ByClientID(p.Context, resolvedID.ID) if err != nil { return nil, err } return se, nil }, }, "querySearch": &graphql.Field{ Args: graphql.FieldConfigArgument{ "query": &graphql.ArgumentConfig{ Type: graphql.String, DefaultValue: "", Description: "query filters the result to only tasks that contain particular terms in their title or description", }, }, Type: api.SearchAPI.Type, Resolve: func(p graphql.ResolveParams) (interface{}, error) { span := trace.FromContext(p.Context).NewChild("trythings.spaceAPI.querySearch") defer span.Finish() sp, ok := p.Source.(*Space) if !ok { return nil, errors.New("expected a space source") } q, ok := p.Args["query"].(string) if !ok { q = "" // Return all tasks. } return &Search{ Query: q, SpaceID: sp.ID, }, nil }, }, "view": &graphql.Field{ Args: graphql.FieldConfigArgument{ "id": &graphql.ArgumentConfig{ Type: graphql.String, DefaultValue: "", Description: "id can be omitted, which will have view resolve to the space's default view.", }, }, Type: api.ViewAPI.Type, Resolve: func(p graphql.ResolveParams) (interface{}, error) { span := trace.FromContext(p.Context).NewChild("trythings.spaceAPI.view") defer span.Finish() id, ok := p.Args["id"].(string) if ok { resolvedID := relay.FromGlobalID(id) if resolvedID == nil { return nil, fmt.Errorf("invalid id %q", id) } v, err := api.ViewService.ByID(p.Context, resolvedID.ID) if err != nil { return nil, err } return v, nil } sp, ok := p.Source.(*Space) if !ok { return nil, errors.New("expected space source") } vs, err := api.ViewService.BySpace(p.Context, sp) if err != nil { return nil, err } if len(vs) == 0 { return nil, errors.New("could not find default view for space") } return vs[0], nil }, }, "views": &graphql.Field{ Type: graphql.NewList(api.ViewAPI.Type), Resolve: func(p graphql.ResolveParams) (interface{}, error) { span := trace.FromContext(p.Context).NewChild("trythings.spaceAPI.views") defer span.Finish() sp, ok := p.Source.(*Space) if !ok { return nil, errors.New("expected space source") } vs, err := api.ViewService.BySpace(p.Context, sp) if err != nil { return nil, err } return vs, nil }, }, }, Interfaces: []*graphql.Interface{ api.NodeInterface, }, }) return nil }
func init() { /** * We get the node interface and field from the Relay library. * * The first method defines the way we resolve an ID to its object. * The second defines the way we resolve an object to its GraphQL type. */ nodeDefinitions = relay.NewNodeDefinitions(relay.NodeDefinitionsConfig{ IDFetcher: func(id string, info graphql.ResolveInfo, ct context.Context) (interface{}, error) { resolvedID := relay.FromGlobalID(id) if resolvedID.Type == "User" { return GetUser(resolvedID.ID), nil } if resolvedID.Type == "Widget" { return GetWidget(resolvedID.ID), nil } return nil, nil }, TypeResolve: func(value interface{}, info graphql.ResolveInfo) *graphql.Object { switch value.(type) { case *User: return userType case *Widget: return widgetType } return nil }, }) /** * Define your own types here */ widgetType = graphql.NewObject(graphql.ObjectConfig{ Name: "Widget", Description: "A shiny widget'", Fields: graphql.Fields{ "id": relay.GlobalIDField("Widget", nil), "name": &graphql.Field{ Description: "The name of the widget", Type: graphql.String, }, }, Interfaces: []*graphql.Interface{ nodeDefinitions.NodeInterface, }, }) widgetConnection = relay.ConnectionDefinitions(relay.ConnectionConfig{ Name: "WidgetConnection", NodeType: widgetType, }) userType = graphql.NewObject(graphql.ObjectConfig{ Name: "User", Description: "A person who uses our app", Fields: graphql.Fields{ "id": relay.GlobalIDField("User", nil), "widgets": &graphql.Field{ Type: widgetConnection.ConnectionType, Description: "A person's collection of widgets", Args: relay.ConnectionArgs, Resolve: func(p graphql.ResolveParams) (interface{}, error) { args := relay.NewConnectionArguments(p.Args) dataSlice := WidgetsToInterfaceSlice(GetWidgets()...) return relay.ConnectionFromArray(dataSlice, args), nil }, }, }, Interfaces: []*graphql.Interface{ nodeDefinitions.NodeInterface, }, }) /** * This is the type that will be the root of our query, * and the entry point into our schema. */ queryType := graphql.NewObject(graphql.ObjectConfig{ Name: "Query", Fields: graphql.Fields{ "node": nodeDefinitions.NodeField, // Add you own root fields here "viewer": &graphql.Field{ Type: userType, Resolve: func(p graphql.ResolveParams) (interface{}, error) { return GetViewer(), nil }, }, }, }) /** * This is the type that will be the root of our mutations, * and the entry point into performing writes in our schema. */ // mutationType := graphql.NewObject(graphql.ObjectConfig{ // Name: "Mutation", // Fields: graphql.Fields{ // // Add you own mutations here // }, // }) /** * Finally, we construct our schema (whose starting query type is the query * type we defined above) and export it. */ var err error Schema, err = graphql.NewSchema(graphql.SchemaConfig{ Query: queryType, }) if err != nil { panic(err) } }
func init() { nodeDefinitions = relay.NewNodeDefinitions(relay.NodeDefinitionsConfig{ IDFetcher: func(id string, info graphql.ResolveInfo) interface{} { resolvedID := relay.FromGlobalID(id) if resolvedID.Type == "Todo" { return GetTodo(resolvedID.ID) } if resolvedID.Type == "User" { return GetUser(resolvedID.ID) } return nil }, TypeResolve: func(value interface{}, info graphql.ResolveInfo) *graphql.Object { switch value.(type) { case *Todo: return todoType case *User: return userType default: return userType } }, }) todoType = graphql.NewObject(graphql.ObjectConfig{ Name: "Todo", Fields: graphql.Fields{ "id": relay.GlobalIDField("Todo", nil), "text": &graphql.Field{ Type: graphql.String, }, "complete": &graphql.Field{ Type: graphql.Boolean, }, }, Interfaces: []*graphql.Interface{nodeDefinitions.NodeInterface}, }) todosConnection = relay.ConnectionDefinitions(relay.ConnectionConfig{ Name: "Todo", NodeType: todoType, }) userType = graphql.NewObject(graphql.ObjectConfig{ Name: "User", Fields: graphql.Fields{ "id": relay.GlobalIDField("User", nil), "todos": &graphql.Field{ Type: todosConnection.ConnectionType, Args: relay.NewConnectionArgs(graphql.FieldConfigArgument{ "status": &graphql.ArgumentConfig{ Type: graphql.String, DefaultValue: "any", }, }), Resolve: func(p graphql.ResolveParams) interface{} { status, _ := p.Args["status"].(string) args := relay.NewConnectionArguments(p.Args) todos := TodosToSliceInterface(GetTodos(status)) return relay.ConnectionFromArray(todos, args) }, }, "totalCount": &graphql.Field{ Type: graphql.Int, Resolve: func(p graphql.ResolveParams) interface{} { return len(GetTodos("any")) }, }, "completedCount": &graphql.Field{ Type: graphql.Int, Resolve: func(p graphql.ResolveParams) interface{} { return len(GetTodos("completed")) }, }, }, Interfaces: []*graphql.Interface{nodeDefinitions.NodeInterface}, }) rootType := graphql.NewObject(graphql.ObjectConfig{ Name: "Root", Fields: graphql.Fields{ "viewer": &graphql.Field{ Type: userType, Resolve: func(p graphql.ResolveParams) interface{} { return GetViewer() }, }, "node": nodeDefinitions.NodeField, }, }) addTodoMutation := relay.MutationWithClientMutationID(relay.MutationConfig{ Name: "AddTodo", InputFields: graphql.InputObjectConfigFieldMap{ "text": &graphql.InputObjectFieldConfig{ Type: graphql.NewNonNull(graphql.String), }, }, OutputFields: graphql.Fields{ "todoEdge": &graphql.Field{ Type: todosConnection.EdgeType, Resolve: func(p graphql.ResolveParams) interface{} { payload, _ := p.Source.(map[string]interface{}) todoId, _ := payload["todoId"].(string) todo := GetTodo(todoId) return relay.EdgeType{ Node: todo, Cursor: relay.CursorForObjectInConnection(TodosToSliceInterface(GetTodos("any")), todo), } }, }, "viewer": &graphql.Field{ Type: userType, Resolve: func(p graphql.ResolveParams) interface{} { return GetViewer() }, }, }, MutateAndGetPayload: func(inputMap map[string]interface{}, info graphql.ResolveInfo) map[string]interface{} { text, _ := inputMap["text"].(string) todoId := AddTodo(text, false) return map[string]interface{}{ "todoId": todoId, } }, }) changeTodoStatusMutation := relay.MutationWithClientMutationID(relay.MutationConfig{ Name: "ChangeTodoStatus", InputFields: graphql.InputObjectConfigFieldMap{ "id": &graphql.InputObjectFieldConfig{ Type: graphql.NewNonNull(graphql.ID), }, "complete": &graphql.InputObjectFieldConfig{ Type: graphql.NewNonNull(graphql.Boolean), }, }, OutputFields: graphql.Fields{ "todo": &graphql.Field{ Type: todoType, Resolve: func(p graphql.ResolveParams) interface{} { payload, _ := p.Source.(map[string]interface{}) todoId, _ := payload["todoId"].(string) todo := GetTodo(todoId) return todo }, }, "viewer": &graphql.Field{ Type: userType, Resolve: func(p graphql.ResolveParams) interface{} { return GetViewer() }, }, }, MutateAndGetPayload: func(inputMap map[string]interface{}, info graphql.ResolveInfo) map[string]interface{} { id, _ := inputMap["id"].(string) complete, _ := inputMap["complete"].(bool) resolvedId := relay.FromGlobalID(id) ChangeTodoStatus(resolvedId.ID, complete) return map[string]interface{}{ "todoId": resolvedId.ID, } }, }) markAllTodosMutation := relay.MutationWithClientMutationID(relay.MutationConfig{ Name: "MarkAllTodos", InputFields: graphql.InputObjectConfigFieldMap{ "complete": &graphql.InputObjectFieldConfig{ Type: graphql.NewNonNull(graphql.Boolean), }, }, OutputFields: graphql.Fields{ "changedTodos": &graphql.Field{ Type: graphql.NewList(todoType), Resolve: func(p graphql.ResolveParams) interface{} { payload, _ := p.Source.(map[string]interface{}) todoIds, _ := payload["todoIds"].([]string) todos := []*Todo{} for _, todoId := range todoIds { todo := GetTodo(todoId) if todo != nil { todos = append(todos, todo) } } return todos }, }, "viewer": &graphql.Field{ Type: userType, Resolve: func(p graphql.ResolveParams) interface{} { return GetViewer() }, }, }, MutateAndGetPayload: func(inputMap map[string]interface{}, info graphql.ResolveInfo) map[string]interface{} { complete, _ := inputMap["complete"].(bool) todoIds := MarkAllTodos(complete) return map[string]interface{}{ "todoIds": todoIds, } }, }) removeCompletedTodosMutation := relay.MutationWithClientMutationID(relay.MutationConfig{ Name: "RemoveCompletedTodos", OutputFields: graphql.Fields{ "deletedTodoIds": &graphql.Field{ Type: graphql.NewList(graphql.String), Resolve: func(p graphql.ResolveParams) interface{} { payload, _ := p.Source.(map[string]interface{}) return payload["todoIds"] }, }, "viewer": &graphql.Field{ Type: userType, Resolve: func(p graphql.ResolveParams) interface{} { return GetViewer() }, }, }, MutateAndGetPayload: func(inputMap map[string]interface{}, info graphql.ResolveInfo) map[string]interface{} { todoIds := RemoveCompletedTodos() return map[string]interface{}{ "todoIds": todoIds, } }, }) removeTodoMutation := relay.MutationWithClientMutationID(relay.MutationConfig{ Name: "RemoveTodo", InputFields: graphql.InputObjectConfigFieldMap{ "id": &graphql.InputObjectFieldConfig{ Type: graphql.NewNonNull(graphql.ID), }, }, OutputFields: graphql.Fields{ "deletedTodoId": &graphql.Field{ Type: graphql.ID, Resolve: func(p graphql.ResolveParams) interface{} { payload, _ := p.Source.(map[string]interface{}) return payload["todoId"] }, }, "viewer": &graphql.Field{ Type: userType, Resolve: func(p graphql.ResolveParams) interface{} { return GetViewer() }, }, }, MutateAndGetPayload: func(inputMap map[string]interface{}, info graphql.ResolveInfo) map[string]interface{} { id, _ := inputMap["id"].(string) resolvedId := relay.FromGlobalID(id) RemoveTodo(resolvedId.ID) return map[string]interface{}{ "todoId": resolvedId.ID, } }, }) renameTodoMutation := relay.MutationWithClientMutationID(relay.MutationConfig{ Name: "RenameTodo", InputFields: graphql.InputObjectConfigFieldMap{ "id": &graphql.InputObjectFieldConfig{ Type: graphql.NewNonNull(graphql.ID), }, "text": &graphql.InputObjectFieldConfig{ Type: graphql.NewNonNull(graphql.String), }, }, OutputFields: graphql.Fields{ "todo": &graphql.Field{ Type: todoType, Resolve: func(p graphql.ResolveParams) interface{} { payload, _ := p.Source.(map[string]interface{}) todoId, _ := payload["todoId"].(string) return GetTodo(todoId) }, }, "viewer": &graphql.Field{ Type: userType, Resolve: func(p graphql.ResolveParams) interface{} { return GetViewer() }, }, }, MutateAndGetPayload: func(inputMap map[string]interface{}, info graphql.ResolveInfo) map[string]interface{} { id, _ := inputMap["id"].(string) resolvedId := relay.FromGlobalID(id) text, _ := inputMap["text"].(string) RenameTodo(resolvedId.ID, text) return map[string]interface{}{ "todoId": resolvedId.ID, } }, }) mutationType := graphql.NewObject(graphql.ObjectConfig{ Name: "Mutation", Fields: graphql.Fields{ "addTodo": addTodoMutation, "changeTodoStatus": changeTodoStatusMutation, "markAllTodos": markAllTodosMutation, "removeCompletedTodos": removeCompletedTodosMutation, "removeTodo": removeTodoMutation, "renameTodo": renameTodoMutation, }, }) var err error Schema, err = graphql.NewSchema(graphql.SchemaConfig{ Query: rootType, Mutation: mutationType, }) if err != nil { panic(err) } }