From 3da160afc2e9a6ba8ec1071a62b73647cc1d26a2 Mon Sep 17 00:00:00 2001 From: gilex-dev Date: Tue, 10 Oct 2023 22:26:07 +0200 Subject: [PATCH] add sqlite storage, implement CRUD queries --- .YetAnotherToDoList.yaml | 1 + CMD_HISTORY.md | 6 + INSTALL.md | 54 +- README.md | 6 +- cmd/root.go | 47 +- cmd/server.go | 29 +- database/main.go | 357 +++++++++++ example.graphql | 77 +++ globals/globals.go | 10 +- go.mod | 22 +- go.sum | 56 +- gqlgen.yml | 4 + graph/generated.go | 1195 ++++++++++++++++++++++++++++++++++++- graph/model/models_gen.go | 23 +- graph/resolver.go | 3 - graph/schema.graphqls | 29 +- graph/schema.resolvers.go | 75 ++- server/main.go | 7 +- 18 files changed, 1885 insertions(+), 116 deletions(-) create mode 100644 database/main.go create mode 100644 example.graphql diff --git a/.YetAnotherToDoList.yaml b/.YetAnotherToDoList.yaml index 932fd84..ff9f05a 100644 --- a/.YetAnotherToDoList.yaml +++ b/.YetAnotherToDoList.yaml @@ -1,3 +1,4 @@ +sqlite3_file: "YetAnotherToDoList.sqlite3" log_file: "YetAnotherToDoList.log" log_UTC: false port: 4242 diff --git a/CMD_HISTORY.md b/CMD_HISTORY.md index 512ac1d..1a2a437 100644 --- a/CMD_HISTORY.md +++ b/CMD_HISTORY.md @@ -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 + ``` \ No newline at end of file diff --git a/INSTALL.md b/INSTALL.md index bcf54fe..7c34f26 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -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=` +You can specify the installation location by adding `GOBIN=` -## 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= @@ -37,17 +43,37 @@ RUN_FLAGS= 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 ` +For other shells or more info, see + +```bash +YetAnotherToDoList help completion +``` + +### 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 diff --git a/README.md b/README.md index 856b90b..6222e1b 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/cmd/root.go b/cmd/root.go index 1fb21d8..81918a9 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -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) } diff --git a/cmd/server.go b/cmd/server.go index fdc5b30..f68e9e6 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -1,15 +1,26 @@ /* -Copyright © 2023 NAME HERE +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 . */ 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")) }, } diff --git a/database/main.go b/database/main.go new file mode 100644 index 0000000..ed12616 --- /dev/null +++ b/database/main.go @@ -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 . +*/ +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 +} diff --git a/example.graphql b/example.graphql new file mode 100644 index 0000000..3f1b0c2 --- /dev/null +++ b/example.graphql @@ -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 + } +} diff --git a/globals/globals.go b/globals/globals.go index e3f43d6..8a2939d 100644 --- a/globals/globals.go +++ b/globals/globals.go @@ -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 . */ - 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 diff --git a/go.mod b/go.mod index 317d171..df39274 100644 --- a/go.mod +++ b/go.mod @@ -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 ) diff --git a/go.sum b/go.sum index 1fbb25b..3dc942d 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/gqlgen.yml b/gqlgen.yml index 40af64d..49f96e7 100644 --- a/gqlgen.yml +++ b/gqlgen.yml @@ -104,3 +104,7 @@ models: fields: user: resolver: true + User: + fields: + todos: + resolver: true diff --git a/graph/generated.go b/graph/generated.go index 931fc74..84e3619 100644 --- a/graph/generated.go +++ b/graph/generated.go @@ -40,6 +40,7 @@ type ResolverRoot interface { Mutation() MutationResolver Query() QueryResolver Todo() TodoResolver + User() UserResolver } type DirectiveRoot struct { @@ -48,10 +49,18 @@ type DirectiveRoot struct { type ComplexityRoot struct { Mutation struct { CreateTodo func(childComplexity int, input model.NewTodo) int + CreateUser func(childComplexity int, input model.NewUser) int + DeleteTodo func(childComplexity int, id string) int + DeleteUser func(childComplexity int, id string) int + UpdateTodo func(childComplexity int, id string, changes model.UpdateTodo) int + UpdateUser func(childComplexity int, id string, changes model.UpdateUser) int } Query struct { + Todo func(childComplexity int, id string) int Todos func(childComplexity int) int + User func(childComplexity int, id string) int + Users func(childComplexity int) int } Todo struct { @@ -62,20 +71,33 @@ type ComplexityRoot struct { } User struct { - ID func(childComplexity int) int - Name func(childComplexity int) int + FullName func(childComplexity int) int + ID func(childComplexity int) int + Todos func(childComplexity int) int + UserName func(childComplexity int) int } } type MutationResolver interface { + CreateUser(ctx context.Context, input model.NewUser) (*model.User, error) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) + UpdateTodo(ctx context.Context, id string, changes model.UpdateTodo) (*model.Todo, error) + UpdateUser(ctx context.Context, id string, changes model.UpdateUser) (*model.User, error) + DeleteUser(ctx context.Context, id string) (*string, error) + DeleteTodo(ctx context.Context, id string) (*string, error) } type QueryResolver interface { Todos(ctx context.Context) ([]*model.Todo, error) + Users(ctx context.Context) ([]*model.User, error) + User(ctx context.Context, id string) (*model.User, error) + Todo(ctx context.Context, id string) (*model.Todo, error) } type TodoResolver interface { User(ctx context.Context, obj *model.Todo) (*model.User, error) } +type UserResolver interface { + Todos(ctx context.Context, obj *model.User) ([]*model.Todo, error) +} type executableSchema struct { resolvers ResolverRoot @@ -104,6 +126,78 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.CreateTodo(childComplexity, args["input"].(model.NewTodo)), true + case "Mutation.createUser": + if e.complexity.Mutation.CreateUser == nil { + break + } + + args, err := ec.field_Mutation_createUser_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.CreateUser(childComplexity, args["input"].(model.NewUser)), true + + case "Mutation.deleteTodo": + if e.complexity.Mutation.DeleteTodo == nil { + break + } + + args, err := ec.field_Mutation_deleteTodo_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.DeleteTodo(childComplexity, args["id"].(string)), true + + case "Mutation.deleteUser": + if e.complexity.Mutation.DeleteUser == nil { + break + } + + args, err := ec.field_Mutation_deleteUser_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.DeleteUser(childComplexity, args["id"].(string)), true + + case "Mutation.updateTodo": + if e.complexity.Mutation.UpdateTodo == nil { + break + } + + args, err := ec.field_Mutation_updateTodo_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.UpdateTodo(childComplexity, args["id"].(string), args["changes"].(model.UpdateTodo)), true + + case "Mutation.updateUser": + if e.complexity.Mutation.UpdateUser == nil { + break + } + + args, err := ec.field_Mutation_updateUser_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.UpdateUser(childComplexity, args["id"].(string), args["changes"].(model.UpdateUser)), true + + case "Query.todo": + if e.complexity.Query.Todo == nil { + break + } + + args, err := ec.field_Query_todo_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.Todo(childComplexity, args["id"].(string)), true + case "Query.todos": if e.complexity.Query.Todos == nil { break @@ -111,6 +205,25 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.Todos(childComplexity), true + case "Query.user": + if e.complexity.Query.User == nil { + break + } + + args, err := ec.field_Query_user_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.User(childComplexity, args["id"].(string)), true + + case "Query.users": + if e.complexity.Query.Users == nil { + break + } + + return e.complexity.Query.Users(childComplexity), true + case "Todo.done": if e.complexity.Todo.Done == nil { break @@ -139,6 +252,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Todo.User(childComplexity), true + case "User.fullName": + if e.complexity.User.FullName == nil { + break + } + + return e.complexity.User.FullName(childComplexity), true + case "User.id": if e.complexity.User.ID == nil { break @@ -146,12 +266,19 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.User.ID(childComplexity), true - case "User.name": - if e.complexity.User.Name == nil { + case "User.todos": + if e.complexity.User.Todos == nil { break } - return e.complexity.User.Name(childComplexity), true + return e.complexity.User.Todos(childComplexity), true + + case "User.userName": + if e.complexity.User.UserName == nil { + break + } + + return e.complexity.User.UserName(childComplexity), true } return 0, false @@ -162,6 +289,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap( ec.unmarshalInputNewTodo, + ec.unmarshalInputNewUser, + ec.unmarshalInputupdateTodo, + ec.unmarshalInputupdateUser, ) first := true @@ -293,6 +423,99 @@ func (ec *executionContext) field_Mutation_createTodo_args(ctx context.Context, return args, nil } +func (ec *executionContext) field_Mutation_createUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 model.NewUser + if tmp, ok := rawArgs["input"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input")) + arg0, err = ec.unmarshalNNewUser2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐNewUser(ctx, tmp) + if err != nil { + return nil, err + } + } + args["input"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Mutation_deleteTodo_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["id"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) + arg0, err = ec.unmarshalNID2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["id"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Mutation_deleteUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["id"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) + arg0, err = ec.unmarshalNID2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["id"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Mutation_updateTodo_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["id"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) + arg0, err = ec.unmarshalNID2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["id"] = arg0 + var arg1 model.UpdateTodo + if tmp, ok := rawArgs["changes"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("changes")) + arg1, err = ec.unmarshalNupdateTodo2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐUpdateTodo(ctx, tmp) + if err != nil { + return nil, err + } + } + args["changes"] = arg1 + return args, nil +} + +func (ec *executionContext) field_Mutation_updateUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["id"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) + arg0, err = ec.unmarshalNID2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["id"] = arg0 + var arg1 model.UpdateUser + if tmp, ok := rawArgs["changes"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("changes")) + arg1, err = ec.unmarshalNupdateUser2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐUpdateUser(ctx, tmp) + if err != nil { + return nil, err + } + } + args["changes"] = arg1 + return args, nil +} + func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -308,6 +531,36 @@ func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs return args, nil } +func (ec *executionContext) field_Query_todo_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["id"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) + arg0, err = ec.unmarshalNID2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["id"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Query_user_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["id"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) + arg0, err = ec.unmarshalNID2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["id"] = arg0 + return args, nil +} + func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -346,6 +599,71 @@ func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArg // region **************************** field.gotpl ***************************** +func (ec *executionContext) _Mutation_createUser(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_createUser(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().CreateUser(rctx, fc.Args["input"].(model.NewUser)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.User) + fc.Result = res + return ec.marshalNUser2ᚖsomepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐUser(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_createUser(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_User_id(ctx, field) + case "userName": + return ec.fieldContext_User_userName(ctx, field) + case "fullName": + return ec.fieldContext_User_fullName(ctx, field) + case "todos": + return ec.fieldContext_User_todos(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type User", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_createUser_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Mutation_createTodo(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Mutation_createTodo(ctx, field) if err != nil { @@ -411,6 +729,240 @@ func (ec *executionContext) fieldContext_Mutation_createTodo(ctx context.Context return fc, nil } +func (ec *executionContext) _Mutation_updateTodo(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_updateTodo(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().UpdateTodo(rctx, fc.Args["id"].(string), fc.Args["changes"].(model.UpdateTodo)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.Todo) + fc.Result = res + return ec.marshalNTodo2ᚖsomepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐTodo(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_updateTodo(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Todo_id(ctx, field) + case "text": + return ec.fieldContext_Todo_text(ctx, field) + case "done": + return ec.fieldContext_Todo_done(ctx, field) + case "user": + return ec.fieldContext_Todo_user(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Todo", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_updateTodo_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_updateUser(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_updateUser(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().UpdateUser(rctx, fc.Args["id"].(string), fc.Args["changes"].(model.UpdateUser)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.User) + fc.Result = res + return ec.marshalNUser2ᚖsomepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐUser(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_updateUser(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_User_id(ctx, field) + case "userName": + return ec.fieldContext_User_userName(ctx, field) + case "fullName": + return ec.fieldContext_User_fullName(ctx, field) + case "todos": + return ec.fieldContext_User_todos(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type User", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_updateUser_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_deleteUser(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_deleteUser(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().DeleteUser(rctx, fc.Args["id"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOID2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_deleteUser(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_deleteUser_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_deleteTodo(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_deleteTodo(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().DeleteTodo(rctx, fc.Args["id"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOID2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_deleteTodo(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_deleteTodo_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Query_todos(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query_todos(ctx, field) if err != nil { @@ -465,6 +1017,190 @@ func (ec *executionContext) fieldContext_Query_todos(ctx context.Context, field return fc, nil } +func (ec *executionContext) _Query_users(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_users(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().Users(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.User) + fc.Result = res + return ec.marshalNUser2ᚕᚖsomepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐUserᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_users(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_User_id(ctx, field) + case "userName": + return ec.fieldContext_User_userName(ctx, field) + case "fullName": + return ec.fieldContext_User_fullName(ctx, field) + case "todos": + return ec.fieldContext_User_todos(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type User", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_user(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_user(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().User(rctx, fc.Args["id"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.User) + fc.Result = res + return ec.marshalNUser2ᚖsomepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐUser(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_user(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_User_id(ctx, field) + case "userName": + return ec.fieldContext_User_userName(ctx, field) + case "fullName": + return ec.fieldContext_User_fullName(ctx, field) + case "todos": + return ec.fieldContext_User_todos(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type User", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_user_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_todo(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_todo(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().Todo(rctx, fc.Args["id"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.Todo) + fc.Result = res + return ec.marshalNTodo2ᚖsomepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐTodo(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_todo(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Todo_id(ctx, field) + case "text": + return ec.fieldContext_Todo_text(ctx, field) + case "done": + return ec.fieldContext_Todo_done(ctx, field) + case "user": + return ec.fieldContext_Todo_user(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Todo", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_todo_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query___type(ctx, field) if err != nil { @@ -767,8 +1503,12 @@ func (ec *executionContext) fieldContext_Todo_user(ctx context.Context, field gr switch field.Name { case "id": return ec.fieldContext_User_id(ctx, field) - case "name": - return ec.fieldContext_User_name(ctx, field) + case "userName": + return ec.fieldContext_User_userName(ctx, field) + case "fullName": + return ec.fieldContext_User_fullName(ctx, field) + case "todos": + return ec.fieldContext_User_todos(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -820,8 +1560,8 @@ func (ec *executionContext) fieldContext_User_id(ctx context.Context, field grap return fc, nil } -func (ec *executionContext) _User_name(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_User_name(ctx, field) +func (ec *executionContext) _User_userName(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_User_userName(ctx, field) if err != nil { return graphql.Null } @@ -834,7 +1574,7 @@ func (ec *executionContext) _User_name(ctx context.Context, field graphql.Collec }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.UserName, nil }) if err != nil { ec.Error(ctx, err) @@ -851,7 +1591,7 @@ func (ec *executionContext) _User_name(ctx context.Context, field graphql.Collec return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_User_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_User_userName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "User", Field: field, @@ -864,6 +1604,104 @@ func (ec *executionContext) fieldContext_User_name(ctx context.Context, field gr return fc, nil } +func (ec *executionContext) _User_fullName(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_User_fullName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.FullName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_User_fullName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "User", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _User_todos(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_User_todos(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.User().Todos(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.Todo) + fc.Result = res + return ec.marshalNTodo2ᚕᚖsomepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐTodoᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_User_todos(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "User", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Todo_id(ctx, field) + case "text": + return ec.fieldContext_Todo_text(ctx, field) + case "done": + return ec.fieldContext_Todo_done(ctx, field) + case "user": + return ec.fieldContext_Todo_user(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Todo", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) ___Directive_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { fc, err := ec.fieldContext___Directive_name(ctx, field) if err != nil { @@ -2664,7 +3502,7 @@ func (ec *executionContext) unmarshalInputNewTodo(ctx context.Context, obj inter var err error ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("userId")) - data, err := ec.unmarshalNString2string(ctx, v) + data, err := ec.unmarshalNID2string(ctx, v) if err != nil { return it, err } @@ -2675,6 +3513,120 @@ func (ec *executionContext) unmarshalInputNewTodo(ctx context.Context, obj inter return it, nil } +func (ec *executionContext) unmarshalInputNewUser(ctx context.Context, obj interface{}) (model.NewUser, error) { + var it model.NewUser + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"userName", "fullName"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "userName": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("userName")) + data, err := ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + it.UserName = data + case "fullName": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("fullName")) + data, err := ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + it.FullName = data + } + } + + return it, nil +} + +func (ec *executionContext) unmarshalInputupdateTodo(ctx context.Context, obj interface{}) (model.UpdateTodo, error) { + var it model.UpdateTodo + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"text", "done"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "text": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("text")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.Text = data + case "done": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("done")) + data, err := ec.unmarshalOBoolean2ᚖbool(ctx, v) + if err != nil { + return it, err + } + it.Done = data + } + } + + return it, nil +} + +func (ec *executionContext) unmarshalInputupdateUser(ctx context.Context, obj interface{}) (model.UpdateUser, error) { + var it model.UpdateUser + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"userName", "fullName"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "userName": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("userName")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.UserName = data + case "fullName": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("fullName")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.FullName = data + } + } + + return it, nil +} + // endregion **************************** input.gotpl ***************************** // region ************************** interface.gotpl *************************** @@ -2702,6 +3654,13 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Mutation") + case "createUser": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_createUser(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } case "createTodo": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Mutation_createTodo(ctx, field) @@ -2709,6 +3668,28 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { out.Invalids++ } + case "updateTodo": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_updateTodo(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "updateUser": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_updateUser(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "deleteUser": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_deleteUser(ctx, field) + }) + case "deleteTodo": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_deleteTodo(ctx, field) + }) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -2772,6 +3753,72 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "users": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_users(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "user": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_user(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "todo": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_todo(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "__type": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { @@ -2903,13 +3950,54 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj case "id": out.Values[i] = ec._User_id(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } - case "name": - out.Values[i] = ec._User_name(ctx, field, obj) + case "userName": + out.Values[i] = ec._User_userName(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } + case "fullName": + out.Values[i] = ec._User_fullName(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "todos": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._User_todos(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -3294,6 +4382,11 @@ func (ec *executionContext) unmarshalNNewTodo2somepiᚗddnsᚗnetᚋgiteaᚋgile return res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) unmarshalNNewUser2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐNewUser(ctx context.Context, v interface{}) (model.NewUser, error) { + res, err := ec.unmarshalInputNewUser(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) { res, err := graphql.UnmarshalString(v) return res, graphql.ErrorOnPath(ctx, err) @@ -3371,6 +4464,50 @@ func (ec *executionContext) marshalNUser2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑd return ec._User(ctx, sel, &v) } +func (ec *executionContext) marshalNUser2ᚕᚖsomepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐUserᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.User) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNUser2ᚖsomepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐUser(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + func (ec *executionContext) marshalNUser2ᚖsomepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐUser(ctx context.Context, sel ast.SelectionSet, v *model.User) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -3634,6 +4771,16 @@ func (ec *executionContext) marshalN__TypeKind2string(ctx context.Context, sel a return res } +func (ec *executionContext) unmarshalNupdateTodo2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐUpdateTodo(ctx context.Context, v interface{}) (model.UpdateTodo, error) { + res, err := ec.unmarshalInputupdateTodo(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) unmarshalNupdateUser2somepiᚗddnsᚗnetᚋgiteaᚋgilexᚑdevᚋYetAnotherToDoListᚋgraphᚋmodelᚐUpdateUser(ctx context.Context, v interface{}) (model.UpdateUser, error) { + res, err := ec.unmarshalInputupdateUser(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + func (ec *executionContext) unmarshalOBoolean2bool(ctx context.Context, v interface{}) (bool, error) { res, err := graphql.UnmarshalBoolean(v) return res, graphql.ErrorOnPath(ctx, err) @@ -3660,6 +4807,22 @@ func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast return res } +func (ec *executionContext) unmarshalOID2ᚖstring(ctx context.Context, v interface{}) (*string, error) { + if v == nil { + return nil, nil + } + res, err := graphql.UnmarshalID(v) + return &res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOID2ᚖstring(ctx context.Context, sel ast.SelectionSet, v *string) graphql.Marshaler { + if v == nil { + return graphql.Null + } + res := graphql.MarshalID(*v) + return res +} + func (ec *executionContext) unmarshalOString2ᚖstring(ctx context.Context, v interface{}) (*string, error) { if v == nil { return nil, nil diff --git a/graph/model/models_gen.go b/graph/model/models_gen.go index bd3d473..54d59ab 100644 --- a/graph/model/models_gen.go +++ b/graph/model/models_gen.go @@ -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"` } diff --git a/graph/resolver.go b/graph/resolver.go index e734407..8d3411c 100644 --- a/graph/resolver.go +++ b/graph/resolver.go @@ -17,13 +17,10 @@ along with this program. If not, see . 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 } diff --git a/graph/schema.graphqls b/graph/schema.graphqls index 173c81a..226b736 100644 --- a/graph/schema.graphqls +++ b/graph/schema.graphqls @@ -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 } diff --git a/graph/schema.resolvers.go b/graph/schema.resolvers.go index f84cfa9..706461b 100644 --- a/graph/schema.resolvers.go +++ b/graph/schema.resolvers.go @@ -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 } diff --git a/server/main.go b/server/main.go index 033438a..32cc1b6 100644 --- a/server/main.go +++ b/server/main.go @@ -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)) }