add sqlite storage, implement CRUD queries

This commit is contained in:
gilex-dev 2023-10-10 22:26:07 +02:00
parent d9cf417e72
commit 3da160afc2
18 changed files with 1885 additions and 116 deletions

View File

@ -1,3 +1,4 @@
sqlite3_file: "YetAnotherToDoList.sqlite3"
log_file: "YetAnotherToDoList.log"
log_UTC: false
port: 4242

View File

@ -31,3 +31,9 @@ Commands were run in the order listed below on a debian based system.
go mod tidy
cobra-cli add server
```
- Add go-sqlite3
```bash
go get github.com/mattn/go-sqlite3
CGO_ENABLED=1 go install github.com/mattn/go-sqlite3 # you may need to install gcc if not already present
go mod tidy
```

View File

@ -1,31 +1,37 @@
---
gitea: none
include_toc: true
---
# Install YetAnotherToDoList
## Requirements:
- go 1.21.1 (not tested, but _should_ also work in all versions since 1.16)
- gcc 4.6+ (according to the [golang wiki](https://github.com/golang/go/wiki/MinimumRequirements#cgo))
## Install via `go install`
> ⚠️ no commit & version information provided
When installing via this method, no commit & version information is provided.
```bash
go install somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList@latest
```
...or specify the installation location by adding `GOBIN=<installation directory>`
You can specify the installation location by adding `GOBIN=<installation directory>`
## Install from git repository
## Install from git repository (Recommended)
Requirements:
- go 1.20
- git
- GNU make
In addition to the [requirements](#requirements), you will need `git` and `GNU make`
```bash
git clone https://somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList.git
cd YetAnotherToDoList
go mod tidy # optional
go mod tidy # only if you want to edit the GraphQL backend
make build
```
To customize your build, you can run `make` with these environment Variables (on the same line):
To customize your build, you can run `make` with these environment variables:
```
INSTALL_DIR=<installation directory>
@ -37,17 +43,37 @@ RUN_FLAGS=<cli arguments for YetAnotherToDoList>
There are currently no pre-build binaries available
## Add shell completion
## Troubleshooting
If you get an error like `Unable to connect: Binary was compiled with 'CGO_ENABLED=0', go-sqlite3 requires cgo to work. This is a stub`, make sure `gcc` is installed and in your `PATH`.
If this still does not work, try
```bash
CGO_ENABLED=1 go install github.com/mattn/go-sqlite3
```
## Post install
### Add shell completion
YetAnotherToDoList must be in your `PATH` for this to work.
### Bash
#### Bash
```bash
source <(YetAnotherToDoList completion bash)
```
For other shells or more info, see `YetAnotherToDoList help completion <bash|zsh|fish|powershell>`
For other shells or more info, see
```bash
YetAnotherToDoList help completion <bash|zsh|fish|powershell>
```
### Configuration
Edit the file [.YetAnotherToDoList.yaml](./.YetAnotherToDoList.yaml) and run `YetAnotherToDoList help` for options.
## Uninstall
@ -57,7 +83,7 @@ Run `make uninstall` or
rm $HOME/.local/bin/YetAnotherToDoList # replace with your installation path
```
> Don't forget to delete the `.log`, `.yaml` and `.sqlite3` files if you don't need them any more.
Don't forget to delete the `.log`, `.yaml` and `.sqlite3` files if you don't need them any more.
### Remove shell completion

View File

@ -1,9 +1,13 @@
# YetAnotherToDoList
A simple To Do List Web Application with Go backend, Vue.js frontend and a GraphQL API
A simple To Do List Web Application with Go backend, Vue.js frontend and a GraphQL API.
This is a simple project I created to learn the [Go](https://go.dev/) language, [GraphQL](https://graphql.org/) and [Vue.js](https://vuejs.org).
Instructions on how to install can be found in [INSTALL.md](./INSTALL.md).
Once installed, you might want to run the queries in [example.graphql](./example.graphql).
## License
[GNU GPLv3 only](./COPYING.md)

View File

@ -24,13 +24,14 @@ import (
"strings"
"time"
_ "github.com/mattn/go-sqlite3"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/database"
"somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/globals"
)
var cfgFile string
var logger *log.Logger
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
@ -66,7 +67,7 @@ func init() {
return
}
}
cobra.OnInitialize(initConfig, initLog)
cobra.OnInitialize(initConfig, initLog, initDB)
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
@ -74,6 +75,7 @@ func init() {
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.YetAnotherToDoList.yaml)")
rootCmd.PersistentFlags().String("log_file", "", "Path to log file")
rootCmd.PersistentFlags().String("sqlite3_file", "", "Path to SQLite3 database")
// Cobra also supports local flags, which will only run
// when this action is called directly.
@ -136,34 +138,51 @@ func initLog() {
}
logger_flags := log.Ldate | log.Ltime | utc
logger = log.New(os.Stdout, "", logger_flags)
globals.Logger = log.New(os.Stdout, "", logger_flags)
if err := viper.BindPFlag("log_file", rootCmd.Flags().Lookup("log_file")); err != nil {
fmt.Println("Unable to bind flag:", err)
}
if viper.GetString("log_file") != "" {
log_path, err := filepath.Abs(viper.GetString("log_file"))
logger.SetOutput(os.Stdout)
globals.Logger.SetOutput(os.Stdout)
if err != nil {
logger.Println("Invalid path for log file", log_path)
globals.Logger.Println("Invalid path for log file", log_path)
}
log_file, err := os.OpenFile(log_path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
fmt.Println("Failed to write to log file:", err)
globals.Logger.Println("Failed to write to log file:", err)
} else {
logger.Println("Switching to log file", log_path)
logger.SetOutput(log_file)
globals.Logger.Println("Switching to log file", log_path)
globals.Logger.SetOutput(log_file)
}
}
logger.SetFlags(0)
logger.Println()
logger.SetFlags(logger_flags)
logger.Printf("Started YetAnotherToDoList with pid: %v\n", os.Getpid())
logger.Printf("Using %v (%v %v) in logs", time_zone_use, time_zone_alt, time_zone_offset)
globals.Logger.SetFlags(0)
globals.Logger.Println()
globals.Logger.SetFlags(logger_flags)
globals.Logger.Printf("Started YetAnotherToDoList with pid: %v\n", os.Getpid())
globals.Logger.Printf("Using %v (%v %v) in logs", time_zone_use, time_zone_alt, time_zone_offset)
}
func initDB() {
if err := viper.BindPFlag("sqlite3_file", rootCmd.Flags().Lookup("sqlite3_file")); err != nil {
fmt.Println("Unable to bind flag:", err)
}
if viper.GetString("sqlite3_file") == "" {
globals.Logger.Fatalln("No SQLite3 file specified")
}
db_path, err := filepath.Abs(viper.GetString("sqlite3_file"))
if err != nil {
globals.Logger.Fatalln("Invalid path for SQLite3 file", db_path)
}
globals.Logger.Println("Connecting to SQLite3", db_path)
globals.DB = database.InitSQLite3(db_path, globals.DB_schema, globals.Logger)
}

View File

@ -1,15 +1,26 @@
/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
YetAnotherToDoList
Copyright © 2023 gilex-dev gilex-dev@proton.me
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cmd
import (
"fmt"
"somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/server"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/globals"
"somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/server"
)
// serverCmd represents the server command
@ -20,14 +31,14 @@ var serverCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
if err := viper.BindPFlag("debug", cmd.Flags().Lookup("debug")); err != nil {
fmt.Println("Unable to bind flag:", err)
globals.Logger.Println("Unable to bind flag:", err)
}
if err := viper.BindPFlag("port", cmd.Flags().Lookup("port")); err != nil {
fmt.Println("Unable to bind flag:", err)
globals.Logger.Println("Unable to bind flag:", err)
}
logger.Println("starting http server...")
server.StartServer(logger, viper.GetInt("port"))
globals.Logger.Println("starting http server...")
server.StartServer(viper.GetInt("port"))
},
}

357
database/main.go Normal file
View File

@ -0,0 +1,357 @@
/*
YetAnotherToDoList
Copyright © 2023 gilex-dev gilex-dev@proton.me
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package database
import (
"database/sql"
"errors"
"fmt"
"log"
"strconv"
"somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/graph/model"
)
type CustomDB struct {
connection *sql.DB
logger *log.Logger
schema uint
}
func InitSQLite3(path string, schema uint, logger *log.Logger) *CustomDB {
db := CustomDB{logger: logger, schema: schema}
var err error
db.connection, err = sql.Open("sqlite3", path)
if err != nil {
db.logger.Fatalln("Unable to open:", err)
}
if err = db.connection.Ping(); err != nil {
db.logger.Fatalln("Unable to connect:", err)
}
var user_version uint
err = db.connection.QueryRow("PRAGMA user_version").Scan(&user_version)
if err != nil {
db.logger.Fatalln("Failed to get database schema version")
}
switch {
case user_version == 0:
db.logger.Println("Initializing empty database")
if err = db.createSQLite3Tables(); err != nil {
db.logger.Fatalln("Error in creating table: ", err)
}
case user_version > db.schema:
db.logger.Fatalln("Incompatible database schema version. Try updating this software.")
case user_version < db.schema:
db.logger.Fatalln("Upgrading database schema currently not supported")
}
return &db
}
func (db CustomDB) createSQLite3Tables() error {
tables := []struct {
name string
sql string
}{
{"User", "userId INTEGER PRIMARY KEY NOT NULL, userName VARCHAR NOT NULL UNIQUE, fullName VARCHAR"},
{"Todo", "todoId INTEGER PRIMARY KEY NOT NULL, text VARCHAR NOT NULL, IS_done BOOL NOT NULL DEFAULT false, FK_User_userId INTEGER NOT NULL, FOREIGN KEY(FK_User_userId) REFERENCES User(userId) ON UPDATE CASCADE ON DELETE CASCADE"},
}
for _, table := range tables {
_, err := db.connection.Exec("CREATE TABLE IF NOT EXISTS " + table.name + " (" + table.sql + ")")
if err != nil {
return err
} else {
db.logger.Println("Successfully created", table.name)
}
}
_, err := db.connection.Exec("PRAGMA foreign_keys = ON")
if err != nil {
db.logger.Fatalln("Failed to enable foreign_keys:", err)
}
_, err = db.connection.Exec("PRAGMA user_version = " + fmt.Sprintf("%d", db.schema))
if err != nil {
db.logger.Fatalln("Failed to set user_version:", err)
}
return nil
}
func (db CustomDB) GetUser(user *model.User) (*model.User, error) {
id, err := strconv.Atoi(user.ID)
if err != nil {
return nil, errors.New("invalid userId")
}
statement, err := db.connection.Prepare("SELECT userName, fullName FROM User WHERE userId = ?")
if err != nil {
return nil, err
}
result := statement.QueryRow(id)
if err := result.Scan(&user.UserName, &user.FullName); err != nil {
return nil, err
}
return user, nil
}
func (db CustomDB) GetTodo(todo *model.Todo) (*model.Todo, error) {
id, err := strconv.Atoi(todo.ID)
if err != nil {
return nil, errors.New("invalid todoId")
}
statement, err := db.connection.Prepare("SELECT text, IS_done FROM Todo WHERE todoId = ?")
if err != nil {
return nil, err
}
result := statement.QueryRow(id)
if err := result.Scan(&todo.Text, &todo.Done); err != nil {
return nil, err
}
return todo, nil
}
func (db CustomDB) GetTodosFrom(user *model.User) ([]*model.Todo, error) {
id, err := strconv.Atoi(user.ID)
if err != nil {
return nil, errors.New("invalid userId")
}
statement, err := db.connection.Prepare("SELECT todoId, text, IS_done FROM Todo WHERE FK_User_userId = ?")
if err != nil {
return nil, err
}
rows, err := statement.Query(id)
if err != nil {
return nil, err
}
defer rows.Close()
var all []*model.Todo
for rows.Next() {
todo := model.Todo{User: user}
if err := rows.Scan(&todo.ID, &todo.Text, &todo.Done); err != nil {
return nil, err
}
all = append(all, &todo)
}
return all, nil
}
func (db CustomDB) GetAllUsers() ([]*model.User, error) {
rows, err := db.connection.Query("SELECT userId, userName, fullName FROM User")
if err != nil {
return nil, err
}
defer rows.Close()
var all []*model.User
for rows.Next() {
var user model.User
if err := rows.Scan(&user.ID, &user.UserName, &user.FullName); err != nil {
return nil, err
}
all = append(all, &user)
}
return all, nil
}
func (db CustomDB) GetAllTodos() ([]*model.Todo, error) {
rows, err := db.connection.Query("SELECT Todo.todoID, Todo.text, Todo.IS_done, User.userID FROM Todo INNER JOIN User ON Todo.FK_User_userID=User.userID")
if err != nil {
return nil, err
}
defer rows.Close()
var todos []*model.Todo
for rows.Next() {
var todo = model.Todo{User: &model.User{}}
if err := rows.Scan(&todo.ID, &todo.Text, &todo.Done, &todo.User.ID); err != nil {
return nil, err
}
todos = append(todos, &todo)
}
return todos, nil
}
func (db CustomDB) AddUser(newUser model.NewUser) (*model.User, error) {
statement, err := db.connection.Prepare("INSERT INTO User (userName, fullName) VALUES (?, ?)")
if err != nil {
return nil, err
}
rows, err := statement.Exec(newUser.UserName, newUser.FullName)
if err != nil {
return nil, err
}
num, err := rows.RowsAffected()
if err != nil {
return nil, err
}
if num < 1 {
return nil, errors.New("no rows affected")
}
insertId, err := rows.LastInsertId()
if err != nil {
return nil, err
}
return &model.User{ID: strconv.FormatInt(insertId, 10), UserName: newUser.UserName, FullName: newUser.FullName}, nil
}
func (db CustomDB) AddTodo(newTodo model.NewTodo) (*model.Todo, error) {
statement, err := db.connection.Prepare("INSERT INTO Todo (text, FK_User_userID) VALUES (?, ?)")
if err != nil {
return nil, err
}
rows, err := statement.Exec(newTodo.Text, newTodo.UserID)
if err != nil {
return nil, err
}
num, err := rows.RowsAffected()
if err != nil {
return nil, err
}
if num < 1 {
return nil, errors.New("no rows affected")
}
insertId, err := rows.LastInsertId()
if err != nil {
return nil, err
}
return &model.Todo{ID: strconv.FormatInt(insertId, 10), Text: newTodo.Text, Done: false}, nil
}
func (db CustomDB) UpdateUser(userId string, changes *model.UpdateUser) (*model.User, error) {
id, err := strconv.Atoi(userId)
if err != nil {
return nil, errors.New("invalid userId")
}
statement, err := db.connection.Prepare("UPDATE User SET userName = IFNULL(?, userName), fullName = IFNULL(?, fullName) WHERE userId = ?")
if err != nil {
return nil, err
}
rows, err := statement.Exec(changes.UserName, changes.FullName, id)
if err != nil {
return nil, err
}
num, err := rows.RowsAffected()
if err != nil {
return nil, err
}
if num < 1 {
return nil, errors.New("no rows affected")
}
return db.GetUser(&model.User{ID: userId})
}
func (db CustomDB) UpdateTodo(todoId string, changes *model.UpdateTodo) (*model.Todo, error) {
id, err := strconv.Atoi(todoId)
if err != nil {
return nil, errors.New("invalid userId")
}
statement, err := db.connection.Prepare("UPDATE Todo SET text = IFNULL(?, text), IS_done = IFNULL(?, IS_done) WHERE todoId = ?")
if err != nil {
return nil, err
}
rows, err := statement.Exec(changes.Text, changes.Done, id)
if err != nil {
return nil, err
}
num, err := rows.RowsAffected()
if err != nil {
return nil, err
}
if num < 1 {
return nil, errors.New("no rows affected")
}
return db.GetTodo(&model.Todo{ID: todoId})
}
func (db CustomDB) DeleteUser(userId string) (*string, error) {
statement, err := db.connection.Prepare("DELETE FROM User WHERE userId = ?")
if err != nil {
return nil, err
}
rows, err := statement.Exec(userId)
if err != nil {
return nil, err
}
num, err := rows.RowsAffected()
if err != nil {
return nil, err
}
if num < 1 {
return nil, errors.New("no rows affected")
}
return &userId, nil
}
func (db CustomDB) DeleteTodo(todoId string) (*string, error) {
statement, err := db.connection.Prepare("DELETE FROM Todo WHERE todoId = ?")
if err != nil {
return nil, err
}
rows, err := statement.Exec(todoId)
if err != nil {
return nil, err
}
num, err := rows.RowsAffected()
if err != nil {
return nil, err
}
if num < 1 {
return nil, errors.New("no rows affected")
}
return &todoId, nil
}

77
example.graphql Normal file
View File

@ -0,0 +1,77 @@
# you first have to add a user to create todos
query getUser {
user(id: 1) {
id
userName
fullName
todos {
id
text
done
}
}
}
query getTodo {
todo(id: 1) {
id
text
done
}
}
query getTodos {
todos {
id
text
done
user {
id
userName
fullName
todos {
id # you could continue this
}
}
}
}
query getUsers {
users {
id
userName
fullName
todos {
id # ...and this too
}
}
}
mutation createTodo {
createTodo(input: { userId: 2, text: "adding a router and CSRF header" }) {
id
text
done
}
}
mutation updateTodo {
updateTodo(id: 1, changes: { done: true }) {
id
text
done
}
}
mutation createUser {
createUser(input: { userName: "1234Lorem", fullName: "Lorem I." }) {
id
}
}
mutation updateUser {
updateUser(id: "1", changes: { fullName: "Lorem Ipsum" }) {
fullName
}
}

View File

@ -14,7 +14,15 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package globals
import (
"log"
"somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/database"
)
var Version, CommitHash string = "unknown", "unknown"
var Logger *log.Logger = log.Default()
var DB *database.CustomDB
var DB_schema uint = 1

22
go.mod
View File

@ -3,35 +3,37 @@ module somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList
go 1.20
require (
github.com/99designs/gqlgen v0.17.36
github.com/99designs/gqlgen v0.17.37
github.com/mattn/go-sqlite3 v1.14.17
github.com/spf13/cobra v1.7.0
github.com/spf13/viper v1.16.0
github.com/vektah/gqlparser/v2 v2.5.8
github.com/vektah/gqlparser/v2 v2.5.9
)
require (
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.3 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.6 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/urfave/cli/v2 v2.25.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/urfave/cli/v2 v2.25.7 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/tools v0.9.3 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.13.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

56
go.sum
View File

@ -36,14 +36,13 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/99designs/gqlgen v0.17.36 h1:u/o/rv2SZ9s5280dyUOOrkpIIkr/7kITMXYD3rkJ9go=
github.com/99designs/gqlgen v0.17.36/go.mod h1:6RdyY8puhCoWAQVr2qzF2OMVfudQzc8ACxzpzluoQm4=
github.com/99designs/gqlgen v0.17.37 h1:PDUH/4AhEYmXb9b1AfxX2JY+myp5TIaoSjNEY7ugt/4=
github.com/99designs/gqlgen v0.17.37/go.mod h1:eov4+h4V+M6snvxWsGsUZskjv9r0vuIrSE7qjMkJYig=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@ -125,6 +124,8 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
@ -132,8 +133,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru/v2 v2.0.3 h1:kmRrRLlInXvng0SmLxmQpQkpbYAvcXm7NPDrgxJa9mE=
github.com/hashicorp/golang-lru/v2 v2.0.3/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/golang-lru/v2 v2.0.6 h1:3xi/Cafd1NaoEnS/yDssIiuVeDVywU0QdFGl3aQaQHM=
github.com/hashicorp/golang-lru/v2 v2.0.6/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@ -151,10 +152,12 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -165,7 +168,6 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
@ -187,14 +189,14 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc=
github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/vektah/gqlparser/v2 v2.5.8 h1:pm6WOnGdzFOCfcQo9L3+xzW51mKrlwTEg4Wr7AH1JW4=
github.com/vektah/gqlparser/v2 v2.5.8/go.mod h1:z8xXUff237NntSuH8mLFijZ+1tjV1swDbpDqjJmk6ME=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/vektah/gqlparser/v2 v2.5.9 h1:bFju9t/E8shqIcDGayKdpl6OHBplFZEeYac9SALiRZE=
github.com/vektah/gqlparser/v2 v2.5.9/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -247,8 +249,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -300,7 +302,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -337,8 +339,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -348,8 +350,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -400,8 +402,8 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -495,15 +497,13 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -104,3 +104,7 @@ models:
fields:
user:
resolver: true
User:
fields:
todos:
resolver: true

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,24 @@ type NewTodo struct {
UserID string `json:"userId"`
}
type User struct {
ID string `json:"id"`
Name string `json:"name"`
type NewUser struct {
UserName string `json:"userName"`
FullName string `json:"fullName"`
}
type User struct {
ID string `json:"id"`
UserName string `json:"userName"`
FullName string `json:"fullName"`
Todos []*Todo `json:"todos"`
}
type UpdateTodo struct {
Text *string `json:"text,omitempty"`
Done *bool `json:"done,omitempty"`
}
type UpdateUser struct {
UserName *string `json:"userName,omitempty"`
FullName *string `json:"fullName,omitempty"`
}

View File

@ -17,13 +17,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package graph
//go:generate go run github.com/99designs/gqlgen generate
import "somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/graph/model"
// This file will not be regenerated automatically.
//
// It serves as dependency injection for your app, add any dependencies you require here.
type Resolver struct {
todos []*model.Todo
getUser *model.User
}

View File

@ -26,18 +26,43 @@ type Todo {
type User {
id: ID!
name: String!
userName: String!
fullName: String!
todos: [Todo!]!
}
type Query {
todos: [Todo!]!
users: [User!]!
user(id: ID!): User!
todo(id: ID!): Todo!
}
input NewUser {
userName: String!
fullName: String!
}
input NewTodo {
text: String!
userId: String!
userId: ID!
}
input updateTodo {
text: String
done: Boolean
}
input updateUser {
userName: String
fullName: String
}
type Mutation {
createUser(input: NewUser!): User!
createTodo(input: NewTodo!): Todo!
updateTodo(id: ID!, changes: updateTodo!): Todo!
updateUser(id: ID!, changes: updateUser!): User!
deleteUser(id: ID!): ID
deleteTodo(id: ID!): ID
}

View File

@ -2,36 +2,85 @@ package graph
// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.
// Code generated by github.com/99designs/gqlgen version v0.17.36
// Code generated by github.com/99designs/gqlgen version v0.17.37
import (
"context"
"crypto/rand"
"math/big"
"errors"
"somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/globals"
"somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/graph/model"
)
// CreateUser is the resolver for the createUser field.
func (r *mutationResolver) CreateUser(ctx context.Context, input model.NewUser) (*model.User, error) {
todo, err := globals.DB.AddUser(input)
if err != nil {
globals.Logger.Println("Failed to add new user:", err)
return nil, errors.New("failed to add new user")
}
return todo, nil
}
// CreateTodo is the resolver for the createTodo field.
func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
rand, _ := rand.Int(rand.Reader, big.NewInt(100))
todo := &model.Todo{
ID: rand.String(),
Text: input.Text,
User: &model.User{ID: input.UserID, Name: "User-" + input.UserID},
todo, err := globals.DB.AddTodo(input)
if err != nil {
globals.Logger.Println("Failed to add new todo:", err)
return nil, errors.New("failed to add new todo")
}
r.todos = append(r.todos, todo)
return todo, nil
}
// UpdateTodo is the resolver for the updateTodo field.
func (r *mutationResolver) UpdateTodo(ctx context.Context, id string, changes model.UpdateTodo) (*model.Todo, error) {
return globals.DB.UpdateTodo(id, &changes)
}
// UpdateUser is the resolver for the updateUser field.
func (r *mutationResolver) UpdateUser(ctx context.Context, id string, changes model.UpdateUser) (*model.User, error) {
return globals.DB.UpdateUser(id, &changes)
}
// DeleteUser is the resolver for the deleteUser field.
func (r *mutationResolver) DeleteUser(ctx context.Context, id string) (*string, error) {
return globals.DB.DeleteUser(id)
}
// DeleteTodo is the resolver for the deleteTodo field.
func (r *mutationResolver) DeleteTodo(ctx context.Context, id string) (*string, error) {
return globals.DB.DeleteTodo(id)
}
// Todos is the resolver for the todos field.
func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) {
return r.todos, nil
return globals.DB.GetAllTodos()
}
// Users is the resolver for the users field.
func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) {
return globals.DB.GetAllUsers()
}
// User is the resolver for the user field.
func (r *queryResolver) User(ctx context.Context, id string) (*model.User, error) {
return globals.DB.GetUser(&model.User{ID: id})
}
// Todo is the resolver for the todo field.
func (r *queryResolver) Todo(ctx context.Context, id string) (*model.Todo, error) {
return globals.DB.GetTodo(&model.Todo{ID: id})
}
// User is the resolver for the user field.
func (r *todoResolver) User(ctx context.Context, obj *model.Todo) (*model.User, error) {
return &model.User{ID: obj.User.ID, Name: obj.User.Name}, nil
// TODO: implement dataloader
return globals.DB.GetUser(obj.User)
}
// Todos is the resolver for the todos field.
func (r *userResolver) Todos(ctx context.Context, obj *model.User) ([]*model.Todo, error) {
return globals.DB.GetTodosFrom(obj)
}
// Mutation returns MutationResolver implementation.
@ -43,6 +92,10 @@ func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
// Todo returns TodoResolver implementation.
func (r *Resolver) Todo() TodoResolver { return &todoResolver{r} }
// User returns UserResolver implementation.
func (r *Resolver) User() UserResolver { return &userResolver{r} }
type mutationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }
type todoResolver struct{ *Resolver }
type userResolver struct{ *Resolver }

View File

@ -18,7 +18,6 @@ package server
import (
"fmt"
"log"
"net/http"
"strconv"
@ -28,7 +27,7 @@ import (
"somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/graph"
)
func StartServer(logger *log.Logger, port int) {
func StartServer(port int) {
srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: &graph.Resolver{}}))
http.Handle("/", playground.Handler("GraphQL playground", "/api"))
@ -37,6 +36,6 @@ func StartServer(logger *log.Logger, port int) {
})
http.Handle("/api", srv)
logger.Printf("connect to http://localhost:%v/ for GraphQL playground", port)
logger.Fatal(http.ListenAndServe(":"+strconv.FormatInt(int64(port), 10), nil))
globals.Logger.Printf("connect to http://localhost:%v/ for GraphQL playground", port)
globals.Logger.Fatal(http.ListenAndServe(":"+strconv.FormatInt(int64(port), 10), nil))
}