add sqlite storage, implement CRUD queries
This commit is contained in:
parent
d9cf417e72
commit
3da160afc2
|
@ -1,3 +1,4 @@
|
|||
sqlite3_file: "YetAnotherToDoList.sqlite3"
|
||||
log_file: "YetAnotherToDoList.log"
|
||||
log_UTC: false
|
||||
port: 4242
|
||||
|
|
|
@ -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
|
||||
```
|
54
INSTALL.md
54
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=<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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
47
cmd/root.go
47
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)
|
||||
}
|
||||
|
|
|
@ -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"))
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
22
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
|
||||
)
|
||||
|
|
56
go.sum
56
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=
|
||||
|
|
|
@ -104,3 +104,7 @@ models:
|
|||
fields:
|
||||
user:
|
||||
resolver: true
|
||||
User:
|
||||
fields:
|
||||
todos:
|
||||
resolver: true
|
||||
|
|
1195
graph/generated.go
1195
graph/generated.go
File diff suppressed because it is too large
Load Diff
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue