Skip to content

Commit

Permalink
Add support for variables in GraphQL queries. #32, #35
Browse files Browse the repository at this point in the history
Far enough to cycle task status!
  • Loading branch information
upvalue committed Nov 24, 2018
1 parent 6b2b3ce commit 1aed558
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 11 deletions.
97 changes: 88 additions & 9 deletions backend/graphql.go
Expand Up @@ -2,11 +2,11 @@
package backend

import (
"encoding/json"
"fmt"
"net/http"
"time"

"github.com/go-macaron/binding"
"github.com/graphql-go/graphql"
macaron "gopkg.in/macaron.v1"
)
Expand Down Expand Up @@ -58,10 +58,12 @@ var taskInterface = graphql.NewObject(graphql.ObjectConfig{
Description: "Task status",
},

"Comment": &graphql.Field{
Type: graphql.NewNonNull(commentInterface),
Description: "Task comment",
},
/*
"Comment": &graphql.Field{
Type: graphql.NewNonNull(commentInterface),
Description: "Task comment",
},
*/

"CompletionRate": &graphql.Field{
Type: graphql.Int,
Expand Down Expand Up @@ -262,6 +264,9 @@ var queryType = graphql.NewObject(graphql.ObjectConfig{
var taskInputObject = graphql.NewInputObject(graphql.InputObjectConfig{
Name: "InputTask",
Fields: graphql.InputObjectConfigFieldMap{
"ID": &graphql.InputObjectFieldConfig{
Type: graphql.Int,
},
"Name": &graphql.InputObjectFieldConfig{
Type: graphql.String,
},
Expand All @@ -271,6 +276,23 @@ var taskInputObject = graphql.NewInputObject(graphql.InputObjectConfig{
"MinutesDelta": &graphql.InputObjectFieldConfig{
Type: graphql.Int,
},
"Scope": &graphql.InputObjectFieldConfig{
Type: graphql.Int,
},
"Status": &graphql.InputObjectFieldConfig{
Type: graphql.Int,
},
// Completely ignored. Defined here in order to allow pass through
// of tasks that result from some queries
"CompletedTasks": &graphql.InputObjectFieldConfig{
Type: graphql.Int,
},
"TotalTasks": &graphql.InputObjectFieldConfig{
Type: graphql.Int,
},
"CompletionRate": &graphql.InputObjectFieldConfig{
Type: graphql.Int,
},
},
})

Expand All @@ -297,6 +319,34 @@ var TaskYnterface = graphql.NewObject(graphql.ObjectConfig{
var mutationType = graphql.NewObject(graphql.ObjectConfig{
Name: "Mutation",
Fields: graphql.Fields{
"updateTask": &graphql.Field{
Type: taskInterface,
Args: graphql.FieldConfigArgument{
"ID": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.Int),
Description: "Task ID",
},
"task": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(taskInputObject),
Description: "Task fields to update",
},
},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
var task Task

DB.Where("id = ?", p.Args["ID"].(int)).Find(&task)

inputTask := p.Args["task"].(map[string]interface{})

if val, ok := inputTask["Status"]; ok {
task.Status = val.(int)
}

DB.Save(&task)

return task, nil
},
},

// Update or add task by name for a particular day
"updateOrAddTaskByName": &graphql.Field{
Expand All @@ -306,7 +356,7 @@ var mutationType = graphql.NewObject(graphql.ObjectConfig{
Type: graphql.NewNonNull(graphql.String),
Description: "Day to add or update task on",
},
"name": &graphql.ArgumentConfig{
"Name": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
Description: "Task name",
},
Expand Down Expand Up @@ -415,6 +465,18 @@ func executeQuery(query string, schema graphql.Schema) *graphql.Result {
return result
}

func executeVarQuery(query string, vars map[string]interface{}, schema graphql.Schema) *graphql.Result {
result := graphql.Do(graphql.Params{
Schema: schema,
RequestString: query,
VariableValues: vars,
})
if len(result.Errors) > 0 {
fmt.Printf("wrong result, unexpected errors: %v", result.Errors)
}
return result
}

// GraphQL schema object
var schema graphql.Schema
var graphqlinitialized = false
Expand All @@ -434,11 +496,28 @@ func graphqlWebInit(m *macaron.Macaron) {
graphqlInitialize()

type Query struct {
Query string `json:"query"`
Query string `json:"query"`
Variables map[string]interface{} `json:"variables"`
}

m.Post("/graphql", binding.Bind(Query{}), func(c *macaron.Context, q Query) {
result := executeQuery(q.Query, schema)
m.Post("/graphql", func(c *macaron.Context) {

var body map[string]interface{}

bodybytes, _ := c.Req.Body().Bytes()
_ = json.Unmarshal(bodybytes, &body)

var result *graphql.Result

if vars, ok := body["variables"]; ok {
result = executeVarQuery(body["query"].(string), vars.(map[string]interface{}), schema)
} else {
result = executeQuery(body["query"].(string), schema)

}

fmt.Printf("%+v\n", body)
// result := executeVarQuery(q.Query, q.Variables, schema)

c.JSON(http.StatusOK, result)
})
Expand Down
13 changes: 13 additions & 0 deletions frontend-ng/src/api/index.ts
Expand Up @@ -52,4 +52,17 @@ export const tasksByDate = (date: string, scopes: ReadonlyArray<RequestScopeEnum
}
}`) as Promise<TasksByDateRequest>;

export const updateTask = (task: Partial<Task>) =>
client.request(`mutation updateTask($taskId: Int!, $task: InputTask!) {
updateTask(ID: $taskId, task: $task) {
ID, Status
}
}`, { task, taskId: task.ID });

export const cycleTaskStatus = (task: Partial<Task>) =>
updateTask({
...task,
Status: ((task.Status || 0) + 1) % (TaskStatus.STATUS_INCOMPLETE + 1)
});

(window as any).tasksByDate = tasksByDate;
5 changes: 5 additions & 0 deletions frontend-ng/src/habits/HabitsPage.scss
Expand Up @@ -74,6 +74,11 @@ main .scope {
//color: $black;
}

&.STATUS_INCOMPLETE {
background-color: red;
color: white;
}

&.STATUS_COMPLETE {
background-color: #449d44;
color: $white;
Expand Down
9 changes: 7 additions & 2 deletions frontend-ng/src/habits/HabitsPage.tsx
Expand Up @@ -2,7 +2,7 @@
import React, { useEffect, useState } from 'react';
import { RouteComponentProps } from '@reach/router';
import classNames from 'classnames';
import { tasksByDate, TasksByDateRequest, TaskStatus } from '../api';
import { tasksByDate, TasksByDateRequest, TaskStatus, cycleTaskStatus } from '../api';

import { MdChevronLeft, MdArrowBack, MdChevronRight, MdArrowForward } from 'react-icons/md';
import { HeaderIconButton } from '../Header';
Expand All @@ -16,14 +16,19 @@ export interface HabitsPageProps extends RouteComponentProps { }
const Task = (props: any) => {
let nameString = props.task && props.task.Name;

// const [task] = useSynchronizedTask(props.task)

//

// const [task] = useSynchronizedTask(props.task)

if (props.task && props.task.CompletedTasks) {
// tslint:disable-next-line
nameString = `${nameString} ${props.task.CompletedTasks}/${props.task.TotalTasks} (${props.task.CompletionRate}%)`
}

const cycleStatus = () => {
console.log('cycle ye task status');
cycleTaskStatus(props.task);
}

return (
Expand Down

0 comments on commit 1aed558

Please sign in to comment.