diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..fc247fd --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Usare IntelliSense per informazioni sui possibili attributi. + // Al passaggio del mouse vengono visualizzate le descrizioni degli attributi esistenti. + // Per altre informazioni, visitare: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "./cmd/webapi" + } + ] +} \ No newline at end of file diff --git a/doc/api.yaml b/doc/api.yaml index 95b2c50..1991525 100644 --- a/doc/api.yaml +++ b/doc/api.yaml @@ -256,15 +256,9 @@ paths: username: $ref: "#/components/schemas/name" followers: - type: array - description: Array of users that the user is following. - items: - $ref: "#/components/schemas/uid_name" + $ref: "#/components/schemas/followers_n" following: - type: array - description: Array of users that are following the user. - items: - $ref: "#/components/schemas/uid_name" + $ref: "#/components/schemas/following_n" photos: $ref: "#/components/schemas/user_photo_stream" '404': diff --git a/service/database/database.go b/service/database/database.go index b93f032..2a48855 100644 --- a/service/database/database.go +++ b/service/database/database.go @@ -55,11 +55,78 @@ func New(db *sql.DB) (AppDatabase, error) { return nil, errors.New("database is required when building a AppDatabase") } - // Check if table exists. If not, the database is empty, and we need to create the structure + // Check if tables exist. If not, the database is empty, and we need to create the structure var tableName string - err := db.QueryRow(`SELECT name FROM sqlite_master WHERE type='table' AND name='example_table';`).Scan(&tableName) + err := db.QueryRow(`SELECT name FROM sqlite_master WHERE type='table' AND name='users';`).Scan(&tableName) //todo: check for all the tables if errors.Is(err, sql.ErrNoRows) { - sqlStmt := `CREATE TABLE example_table (id INTEGER NOT NULL PRIMARY KEY, name TEXT);` + sqlStmt := `CREATE TABLE "users" ( + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL UNIQUE, + PRIMARY KEY("uid") + )` //todo: one query is enough! Why do I need to do this? + _, err = db.Exec(sqlStmt) + if err != nil { + return nil, fmt.Errorf("error creating database structure: %w", err) + } + sqlStmt = `CREATE TABLE "follows" ( + "follower" TEXT NOT NULL, + "followed" TEXT NOT NULL, + FOREIGN KEY("follower") REFERENCES "users"("uid") ON UPDATE CASCADE, + FOREIGN KEY("followed") REFERENCES "users"("uid") ON UPDATE CASCADE, + PRIMARY KEY("follower","followed") + )` + _, err = db.Exec(sqlStmt) + if err != nil { + return nil, fmt.Errorf("error creating database structure: %w", err) + } + sqlStmt = `CREATE TABLE "bans" ( + "user" TEXT NOT NULL, + "ban" TEXT NOT NULL, + FOREIGN KEY("user") REFERENCES "users"("uid") ON UPDATE CASCADE, + FOREIGN KEY("ban") REFERENCES "users"("uid") ON UPDATE CASCADE, + PRIMARY KEY("user","ban") + )` + _, err = db.Exec(sqlStmt) + if err != nil { + return nil, fmt.Errorf("error creating database structure: %w", err) + } + sqlStmt = `CREATE TABLE "photos" ( + "user" TEXT NOT NULL, + "id" INTEGER NOT NULL, + "date" TEXT NOT NULL, + FOREIGN KEY("user") REFERENCES "users"("uid") ON UPDATE CASCADE, + PRIMARY KEY("id" AUTOINCREMENT) + )` + _, err = db.Exec(sqlStmt) + if err != nil { + return nil, fmt.Errorf("error creating database structure: %w", err) + } + sqlStmt = `CREATE TABLE "comments" ( + "user" TEXT NOT NULL, + "photo" INTEGER NOT NULL, + "id" INTEGER NOT NULL, + "comment" TEXT NOT NULL, + "date" TEXT NOT NULL, + FOREIGN KEY("user") REFERENCES "users"("uid"), + PRIMARY KEY("id" AUTOINCREMENT), + FOREIGN KEY("photo") REFERENCES "photos"("id") + );` + _, err = db.Exec(sqlStmt) + if err != nil { + return nil, fmt.Errorf("error creating database structure: %w", err) + } + sqlStmt = `CREATE TABLE "likes" ( + "user" TEXT NOT NULL, + "photo_id" INTEGER NOT NULL, + FOREIGN KEY("user") REFERENCES "users"("uid") ON UPDATE CASCADE, + FOREIGN KEY("photo_id") REFERENCES "photos"("id") ON UPDATE CASCADE, + PRIMARY KEY("user","photo_id") + )` + _, err = db.Exec(sqlStmt) + if err != nil { + return nil, fmt.Errorf("error creating database structure: %w", err) + } + sqlStmt = `PRAGMA foreign_keys = ON` _, err = db.Exec(sqlStmt) if err != nil { return nil, fmt.Errorf("error creating database structure: %w", err) diff --git a/service/database/db-photos.go b/service/database/db-photos.go new file mode 100644 index 0000000..f531546 --- /dev/null +++ b/service/database/db-photos.go @@ -0,0 +1,53 @@ +package database + +import "time" + +type Photo struct { + ID int64 + Likes int64 + Comments int64 +} + +type UserProfile struct { + UID string + Name string + Following []string + Followers []string + Photos []Photo +} + +// Post a new photo +func (db *appdbimpl) PostPhoto(uid string) (int64, error) { + res, err := db.c.Exec(`INSERT INTO "photos" ("user", "date") VALUES (?, ?)`, uid, time.Now().Format(time.RFC3339)) + if err != nil { + return 0, err + } + id, err := res.LastInsertId() + if err != nil { + return 0, err + } + return id, nil +} + +// Get user profile, including username, followers, following, and photos +func (db *appdbimpl) GetUserProfile(uid string) (*UserProfile, error) { + // Get user info + var name string + err := db.c.QueryRow(`SELECT "name" FROM "users" WHERE "uid" = ?`, uid).Scan(&name) + if err != nil { + return nil, err + } + // Get followers +} + +// Like a photo +func (db *appdbimpl) LikePhoto(uid string, photo int64) error { + _, err := db.c.Exec(`INSERT INTO "likes" ("user", "photo_id") VALUES (?, ?)`, uid, photo) + return err +} + +// Unlike a photo +func (db *appdbimpl) UnlikePhoto(uid string, photo int64) error { + _, err := db.c.Exec(`DELETE FROM "likes" WHERE "user" = ? AND "photo_id" = ?`, uid, photo) + return err +} diff --git a/service/database/db-users.go b/service/database/db-users.go new file mode 100644 index 0000000..0093436 --- /dev/null +++ b/service/database/db-users.go @@ -0,0 +1,31 @@ +package database + +// Create a new user +func (db *appdbimpl) CreateUser(uid string, name string) error { + _, err := db.c.Exec(`INSERT INTO "users" ("uid", "name") VALUES (?, ?)`, uid, name) + return err +} + +// Follow a user +func (db *appdbimpl) FollowUser(uid string, follow string) error { + _, err := db.c.Exec(`INSERT INTO "follows" ("follower", "followed") VALUES (?, ?)`, uid, follow) + return err +} + +// Unfollow a user +func (db *appdbimpl) UnfollowUser(uid string, unfollow string) error { + _, err := db.c.Exec(`DELETE FROM "follows" WHERE "follower" = ? AND "followed" = ?`, uid, unfollow) + return err +} //todo: should return boolean or something similar + +// Ban a user +func (db *appdbimpl) BanUser(uid string, ban string) error { + _, err := db.c.Exec(`INSERT INTO "bans" ("user", "ban") VALUES (?, ?)`, uid, ban) + return err +} + +// Unban a user +func (db *appdbimpl) UnbanUser(uid string, unban string) error { + _, err := db.c.Exec(`DELETE FROM "bans" WHERE "user" = ? AND "ban" = ?`, uid, unban) + return err +} diff --git a/service/database/get-name.go b/service/database/get-name.go deleted file mode 100644 index de67176..0000000 --- a/service/database/get-name.go +++ /dev/null @@ -1,8 +0,0 @@ -package database - -// GetName is an example that shows you how to query data -func (db *appdbimpl) GetName() (string, error) { - var name string - err := db.c.QueryRow("SELECT name FROM example_table WHERE id=1").Scan(&name) - return name, err -} diff --git a/wasa.db b/wasa.db new file mode 100644 index 0000000..aa3c0d2 Binary files /dev/null and b/wasa.db differ