From 1140ab5be449df05af77a87837138d1f47342561 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 25 Oct 2022 11:47:35 +0100 Subject: [PATCH 01/12] created migrations controllers models user get create --- backend/main/models/user.go | 62 +++++++++++++++++++ backend/main/server/controllers.go | 32 ++++++++++ backend/main/server/routes.go | 4 ++ .../000044_create_user_table.down.sql | 1 + .../000044_create_user_table.up.sql | 11 ++++ 5 files changed, 110 insertions(+) create mode 100644 backend/main/models/user.go create mode 100644 backend/migrations/000044_create_user_table.down.sql create mode 100644 backend/migrations/000044_create_user_table.up.sql diff --git a/backend/main/models/user.go b/backend/main/models/user.go new file mode 100644 index 000000000..eea1110f3 --- /dev/null +++ b/backend/main/models/user.go @@ -0,0 +1,62 @@ +package models + +import ( + "github.com/DapperCollectives/CAST/backend/main/shared" + "github.com/georgysavva/scany/pgxscan" + "github.com/google/uuid" +) + +type User struct { + Uuid *string `json:"uuid,omitempty"` + Addr *string `json:"address"` + Profile_image *string `json:"profileImage,omitempty"` + Name *string `json:"name,omitempty"` + Website *string `json:"website,omitempty"` + Bio *string `json:"bio,omitempty"` + Twitter *string `json:"twitter,omitempty"` + Discord *string `json:"discord,omitempty"` + Instagram *string `json:"instagram,omitempty"` +} + +func (u *User) CreateUser(db *shared.Database, payload *User) error { + err := db.Conn.QueryRow(db.Context, + `INSERT INTO users (uuid, addr, profile_image, name, website, bio, + twitter, discord, instagram) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) + RETURNING *`, + uuid.New(), + payload.Addr, + payload.Profile_image, + payload.Name, + payload.Website, + payload.Bio, + payload.Twitter, + payload.Discord, + payload.Instagram). + Scan( + &u.Uuid, + &u.Addr, + &u.Profile_image, + &u.Name, + &u.Website, + &u.Bio, + &u.Twitter, + &u.Discord, + &u.Instagram, + ) + + if err != nil { + return err + } + return nil +} + +func (u *User) GetUser(db *shared.Database, address string) error { + err := pgxscan.Get(db.Context, db.Conn, u, + "SELECT * FROM users WHERE address = $1", + address) + if err != nil { + return err + } + return nil +} diff --git a/backend/main/server/controllers.go b/backend/main/server/controllers.go index 913057a0b..3eb5e4788 100644 --- a/backend/main/server/controllers.go +++ b/backend/main/server/controllers.go @@ -894,6 +894,38 @@ func (a *App) createCommunityUser(w http.ResponseWriter, r *http.Request) { respondWithJSON(w, http.StatusCreated, "OK") } +func (a *App) getUser(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + addr := vars["addr"] + + var user models.User + if err := user.GetUser(a.DB, addr); err != nil { + log.Error().Err(err).Msg("Error getting user") + respondWithError(w, errIncompleteRequest) + return + } + + respondWithJSON(w, http.StatusOK, user) +} + +func (a *App) createUser(w http.ResponseWriter, r *http.Request) { + payload := models.User{} + + if err := validatePayload(r.Body, &payload); err != nil { + log.Error().Err(err).Msg("Error validating payload") + respondWithError(w, errIncompleteRequest) + return + } + + var user models.User + if err := user.CreateUser(a.DB, &payload); err != nil { + log.Error().Err(err).Msg("Error creating user") + respondWithError(w, errIncompleteRequest) + return + } + respondWithJSON(w, http.StatusCreated, user) +} + func (a *App) getCommunityUsers(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) communityId, err := strconv.Atoi(vars["communityId"]) diff --git a/backend/main/server/routes.go b/backend/main/server/routes.go index 6dbd0d703..2323345da 100644 --- a/backend/main/server/routes.go +++ b/backend/main/server/routes.go @@ -40,6 +40,10 @@ func (a *App) initializeRoutes() { a.Router.HandleFunc("/voting-strategies", a.getVotingStrategies).Methods("GET") a.Router.HandleFunc("/community-categories", a.getCommunityCategories).Methods("GET") // Users + a.Router.HandleFunc("/user/{addr:0x[a-zA-Z0-9]+}", a.getUser).Methods("GET") + //a.Router.HandleFunc("/user/{addr:0x[a-zA-Z0-9]+}", a.updateUser).Methods("PUT", "OPTIONS") + a.Router.HandleFunc("/user", a.createUser).Methods("POST", "OPTIONS") + a.Router.HandleFunc("/users/{addr:0x[a-zA-Z0-9]{16}}/communities", a.getUserCommunities).Methods("GET") a.Router.HandleFunc("/users/{addr:0x[a-zA-Z0-9]{16}}/proposals", a.getUserProposals).Methods("GET") a.Router.HandleFunc("/communities/{communityId:[0-9]+}/users", a.createCommunityUser).Methods("POST", "OPTIONS") diff --git a/backend/migrations/000044_create_user_table.down.sql b/backend/migrations/000044_create_user_table.down.sql new file mode 100644 index 000000000..c99ddcdc8 --- /dev/null +++ b/backend/migrations/000044_create_user_table.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS users; diff --git a/backend/migrations/000044_create_user_table.up.sql b/backend/migrations/000044_create_user_table.up.sql new file mode 100644 index 000000000..27732969f --- /dev/null +++ b/backend/migrations/000044_create_user_table.up.sql @@ -0,0 +1,11 @@ +CREATE TABLE users ( + uuid UUID PRIMARY KEY NOT NULL, + addr VARCHAR(255), + profile_image TEXT, + name VARCHAR(50), + website VARCHAR(50), + bio VARCHAR(255), + twitter VARCHAR(50), + discord VARCHAR(50), + instagram VARCHAR(50) +); From 2251fae8d933121ffebd6f42e5dc844713df27bd Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 25 Oct 2022 12:01:58 +0100 Subject: [PATCH 02/12] created custom error message --- backend/main/models/user.go | 21 ++++++++++++++++----- backend/main/server/controllers.go | 11 +++++++++-- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/backend/main/models/user.go b/backend/main/models/user.go index eea1110f3..213714313 100644 --- a/backend/main/models/user.go +++ b/backend/main/models/user.go @@ -2,7 +2,6 @@ package models import ( "github.com/DapperCollectives/CAST/backend/main/shared" - "github.com/georgysavva/scany/pgxscan" "github.com/google/uuid" ) @@ -51,10 +50,22 @@ func (u *User) CreateUser(db *shared.Database, payload *User) error { return nil } -func (u *User) GetUser(db *shared.Database, address string) error { - err := pgxscan.Get(db.Context, db.Conn, u, - "SELECT * FROM users WHERE address = $1", - address) +func (u *User) GetUser(db *shared.Database, addr string) error { + err := db.Conn.QueryRow( + db.Context, + "SELECT * FROM users WHERE addr = $1", + addr).Scan( + &u.Uuid, + &u.Addr, + &u.Profile_image, + &u.Name, + &u.Website, + &u.Bio, + &u.Twitter, + &u.Discord, + &u.Instagram, + ) + if err != nil { return err } diff --git a/backend/main/server/controllers.go b/backend/main/server/controllers.go index 3eb5e4788..64e993a04 100644 --- a/backend/main/server/controllers.go +++ b/backend/main/server/controllers.go @@ -122,6 +122,13 @@ var ( Details: "The proposal you are trying to access no longer exists.", } + errUserNotFound = errorResponse{ + StatusCode: http.StatusNotFound, + ErrorCode: "ERR_1015", + Message: "User Not Found", + Details: "The user you are trying to access does not exist.", + } + nilErr = errorResponse{} ) @@ -900,8 +907,8 @@ func (a *App) getUser(w http.ResponseWriter, r *http.Request) { var user models.User if err := user.GetUser(a.DB, addr); err != nil { - log.Error().Err(err).Msg("Error getting user") - respondWithError(w, errIncompleteRequest) + log.Error().Err(err).Msg("Error user not foune") + respondWithError(w, errUserNotFound) return } From 5eba985b4f237722a023cde6f496fb406810b86e Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 25 Oct 2022 12:15:30 +0100 Subject: [PATCH 03/12] created update controller and model --- backend/main/models/user.go | 39 ++++++++++++++++++++++++++++++ backend/main/server/controllers.go | 18 ++++++++++++++ backend/main/server/routes.go | 2 +- 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/backend/main/models/user.go b/backend/main/models/user.go index 213714313..08794be82 100644 --- a/backend/main/models/user.go +++ b/backend/main/models/user.go @@ -71,3 +71,42 @@ func (u *User) GetUser(db *shared.Database, addr string) error { } return nil } + +func (u *User) UpdateUser(db *shared.Database, payload *User) error { + err := db.Conn.QueryRow( + db.Context, + `UPDATE users + SET profile_image = COALESCE($1,profile_image), + name = COALESCE($2, name), + website = COALESCE($3, website), + bio = COALESCE($4, bio), + twitter = COALESCE($5, twitter), + discord = COALESCE($6, discord), + instagram = COALESCE($7, instagram) + WHERE addr = $8 + RETURNING *`, + payload.Profile_image, + payload.Name, + payload.Website, + payload.Bio, + payload.Twitter, + payload.Discord, + payload.Instagram, + payload.Addr, + ).Scan( + &u.Uuid, + &u.Addr, + &u.Profile_image, + &u.Name, + &u.Website, + &u.Bio, + &u.Twitter, + &u.Discord, + &u.Instagram, + ) + + if err != nil { + return err + } + return nil +} diff --git a/backend/main/server/controllers.go b/backend/main/server/controllers.go index 64e993a04..4ea925b57 100644 --- a/backend/main/server/controllers.go +++ b/backend/main/server/controllers.go @@ -933,6 +933,24 @@ func (a *App) createUser(w http.ResponseWriter, r *http.Request) { respondWithJSON(w, http.StatusCreated, user) } +func (a *App) updateUser(w http.ResponseWriter, r *http.Request) { + payload := models.User{} + + if err := validatePayload(r.Body, &payload); err != nil { + log.Error().Err(err).Msg("Error validating payload") + respondWithError(w, errIncompleteRequest) + return + } + + var user models.User + if err := user.UpdateUser(a.DB, &payload); err != nil { + log.Error().Err(err).Msg("Error updating user") + respondWithError(w, errIncompleteRequest) + return + } + respondWithJSON(w, http.StatusOK, user) +} + func (a *App) getCommunityUsers(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) communityId, err := strconv.Atoi(vars["communityId"]) diff --git a/backend/main/server/routes.go b/backend/main/server/routes.go index 2323345da..0cd5d632b 100644 --- a/backend/main/server/routes.go +++ b/backend/main/server/routes.go @@ -41,7 +41,7 @@ func (a *App) initializeRoutes() { a.Router.HandleFunc("/community-categories", a.getCommunityCategories).Methods("GET") // Users a.Router.HandleFunc("/user/{addr:0x[a-zA-Z0-9]+}", a.getUser).Methods("GET") - //a.Router.HandleFunc("/user/{addr:0x[a-zA-Z0-9]+}", a.updateUser).Methods("PUT", "OPTIONS") + a.Router.HandleFunc("/user", a.updateUser).Methods("PUT", "OPTIONS") a.Router.HandleFunc("/user", a.createUser).Methods("POST", "OPTIONS") a.Router.HandleFunc("/users/{addr:0x[a-zA-Z0-9]{16}}/communities", a.getUserCommunities).Methods("GET") From 58101f48190c6f87de4780a36c0296a1e2980f34 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 25 Oct 2022 13:05:45 +0100 Subject: [PATCH 04/12] added basic test cases --- backend/tests/test_utils/user_utils.go | 43 +++++++++++++++++ backend/tests/user_test.go | 66 ++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 backend/tests/test_utils/user_utils.go create mode 100644 backend/tests/user_test.go diff --git a/backend/tests/test_utils/user_utils.go b/backend/tests/test_utils/user_utils.go new file mode 100644 index 000000000..828c26c08 --- /dev/null +++ b/backend/tests/test_utils/user_utils.go @@ -0,0 +1,43 @@ +package test_utils + +import ( + "bytes" + "encoding/json" + "fmt" + + "net/http" + "net/http/httptest" + + "github.com/DapperCollectives/CAST/backend/main/models" +) + +var ( + dummyProfileImage = "https://pbs.twimg.com/profile_images/1277734310/IMG_0001_400x400.JPG" + dummyName = "Test User" + dummyBio = "This is a test bio" + dummyTwitter = "https://twitter.com/testuser" + dummyDiscord = "https://discord.com/testuser" + dummyInstagram = "https://instagram.com/testuser" +) + +func (otu *OverflowTestUtils) CreateUserAPI(user *models.User) *httptest.ResponseRecorder { + json, _ := json.Marshal(user) + req, _ := http.NewRequest("POST", "/user", bytes.NewBuffer(json)) + req.Header.Set("Content-Type", "application/json") + return otu.ExecuteRequest(req) +} + +func (otu *OverflowTestUtils) GenerateUserStruct(signer string) *models.User { + account, _ := otu.O.State.Accounts().ByName(fmt.Sprintf("emulator-%s", signer)) + address := fmt.Sprintf("0x%s", account.Address().String()) + + return &models.User{ + Addr: &address, + Profile_image: &dummyProfileImage, + Name: &dummyName, + Bio: &dummyBio, + Twitter: &dummyTwitter, + Discord: &dummyDiscord, + Instagram: &dummyInstagram, + } +} diff --git a/backend/tests/user_test.go b/backend/tests/user_test.go new file mode 100644 index 000000000..dd06a3c5b --- /dev/null +++ b/backend/tests/user_test.go @@ -0,0 +1,66 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/DapperCollectives/CAST/backend/main/models" + "github.com/stretchr/testify/assert" +) + +func TestUser(t *testing.T) { + clearTable("users") + + t.Run("should be able to create a user", func(t *testing.T) { + userStruct := otu.GenerateUserStruct("account") + response := otu.CreateUserAPI(userStruct) + checkResponseCode(t, http.StatusCreated, response.Code) + + var created models.User + json.Unmarshal(response.Body.Bytes(), &created) + + assert.Equal(t, *userStruct.Addr, *created.Addr) + }) + + t.Run("should be able to get a user", func(t *testing.T) { + userStruct := otu.GenerateUserStruct("account") + response := otu.CreateUserAPI(userStruct) + checkResponseCode(t, http.StatusCreated, response.Code) + + var created models.User + json.Unmarshal(response.Body.Bytes(), &created) + + req, _ := http.NewRequest("GET", fmt.Sprintf("/user/%s", *created.Addr), nil) + response = otu.ExecuteRequest(req) + + var user models.User + json.Unmarshal(response.Body.Bytes(), &user) + + assert.Equal(t, *created.Addr, *user.Addr) + }) + + t.Run("should be able to update a user", func(t *testing.T) { + userStruct := otu.GenerateUserStruct("account") + response := otu.CreateUserAPI(userStruct) + checkResponseCode(t, http.StatusCreated, response.Code) + + var toUpdate models.User + json.Unmarshal(response.Body.Bytes(), &toUpdate) + + updatedName := "Updated Name" + + toUpdate.Name = &updatedName + payload, _ := json.Marshal(toUpdate) + req, _ := http.NewRequest("PUT", fmt.Sprintf("/user"), bytes.NewBuffer(payload)) + req.Header.Set("Content-Type", "application/json") + response = otu.ExecuteRequest(req) + + var updated models.User + json.Unmarshal(response.Body.Bytes(), &updated) + + assert.Equal(t, *toUpdate.Name, *updated.Name) + }) +} From de1b096232d6dea35403f3dfaaa7d2d8f9f67eb4 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 25 Oct 2022 15:19:39 +0100 Subject: [PATCH 05/12] added negative test case --- backend/main/server/controllers.go | 4 ++-- backend/tests/user_test.go | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/backend/main/server/controllers.go b/backend/main/server/controllers.go index 4ea925b57..d68e8a4c0 100644 --- a/backend/main/server/controllers.go +++ b/backend/main/server/controllers.go @@ -125,7 +125,7 @@ var ( errUserNotFound = errorResponse{ StatusCode: http.StatusNotFound, ErrorCode: "ERR_1015", - Message: "User Not Found", + Message: "User not found", Details: "The user you are trying to access does not exist.", } @@ -907,7 +907,7 @@ func (a *App) getUser(w http.ResponseWriter, r *http.Request) { var user models.User if err := user.GetUser(a.DB, addr); err != nil { - log.Error().Err(err).Msg("Error user not foune") + log.Error().Err(err).Msg("Error user not found") respondWithError(w, errUserNotFound) return } diff --git a/backend/tests/user_test.go b/backend/tests/user_test.go index dd06a3c5b..996afe724 100644 --- a/backend/tests/user_test.go +++ b/backend/tests/user_test.go @@ -63,4 +63,16 @@ func TestUser(t *testing.T) { assert.Equal(t, *toUpdate.Name, *updated.Name) }) + + t.Run("should throw an error when getting a user that doesn't exist", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/user/0x69", nil) + response := otu.ExecuteRequest(req) + + checkResponseCode(t, http.StatusNotFound, response.Code) + + var m map[string]string + json.Unmarshal(response.Body.Bytes(), &m) + + assert.Equal(t, "User not found", m["message"]) + }) } From 19124ec7941f298e08ca732123206e96e026cc0f Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 25 Oct 2022 15:28:34 +0100 Subject: [PATCH 06/12] added error test cases --- backend/main/server/controllers.go | 3 ++- backend/tests/test_utils/user_utils.go | 14 ++++++++++++++ backend/tests/user_test.go | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/backend/main/server/controllers.go b/backend/main/server/controllers.go index d68e8a4c0..401256731 100644 --- a/backend/main/server/controllers.go +++ b/backend/main/server/controllers.go @@ -943,9 +943,10 @@ func (a *App) updateUser(w http.ResponseWriter, r *http.Request) { } var user models.User + if err := user.UpdateUser(a.DB, &payload); err != nil { log.Error().Err(err).Msg("Error updating user") - respondWithError(w, errIncompleteRequest) + respondWithError(w, errUserNotFound) return } respondWithJSON(w, http.StatusOK, user) diff --git a/backend/tests/test_utils/user_utils.go b/backend/tests/test_utils/user_utils.go index 828c26c08..902676b71 100644 --- a/backend/tests/test_utils/user_utils.go +++ b/backend/tests/test_utils/user_utils.go @@ -41,3 +41,17 @@ func (otu *OverflowTestUtils) GenerateUserStruct(signer string) *models.User { Instagram: &dummyInstagram, } } + +func (otu *OverflowTestUtils) GenerateFailUserStruct() *models.User { + nonExistentAccount := "0x696969" + + return &models.User{ + Addr: &nonExistentAccount, + Profile_image: &dummyProfileImage, + Name: &dummyName, + Bio: &dummyBio, + Twitter: &dummyTwitter, + Discord: &dummyDiscord, + Instagram: &dummyInstagram, + } +} diff --git a/backend/tests/user_test.go b/backend/tests/user_test.go index 996afe724..5a3c14869 100644 --- a/backend/tests/user_test.go +++ b/backend/tests/user_test.go @@ -75,4 +75,19 @@ func TestUser(t *testing.T) { assert.Equal(t, "User not found", m["message"]) }) + + t.Run("should throw an error when updating a user that doesn't exist", func(t *testing.T) { + userStruct := otu.GenerateFailUserStruct() + payload, _ := json.Marshal(userStruct) + req, _ := http.NewRequest("PUT", "/user", bytes.NewBuffer(payload)) + req.Header.Set("Content-Type", "application/json") + response := otu.ExecuteRequest(req) + + checkResponseCode(t, http.StatusNotFound, response.Code) + + var m map[string]string + json.Unmarshal(response.Body.Bytes(), &m) + + assert.Equal(t, "User not found", m["message"]) + }) } From 63b75acbdd1346c0ef73dcf93c84f1ffbe8d3760 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Thu, 27 Oct 2022 15:36:55 +0100 Subject: [PATCH 07/12] added validate sigs functions and updated tests --- backend/main/models/user.go | 20 ++++++++------- backend/main/server/controllers.go | 10 ++++++++ backend/main/server/helpers.go | 1 - .../000044_create_user_table.up.sql | 2 +- backend/tests/test_utils/user_utils.go | 25 +++++++++++++++++++ backend/tests/user_test.go | 17 +++++++------ 6 files changed, 56 insertions(+), 19 deletions(-) diff --git a/backend/main/models/user.go b/backend/main/models/user.go index 08794be82..a45c63cb6 100644 --- a/backend/main/models/user.go +++ b/backend/main/models/user.go @@ -6,15 +6,17 @@ import ( ) type User struct { - Uuid *string `json:"uuid,omitempty"` - Addr *string `json:"address"` - Profile_image *string `json:"profileImage,omitempty"` - Name *string `json:"name,omitempty"` - Website *string `json:"website,omitempty"` - Bio *string `json:"bio,omitempty"` - Twitter *string `json:"twitter,omitempty"` - Discord *string `json:"discord,omitempty"` - Instagram *string `json:"instagram,omitempty"` + Uuid *string `json:"uuid,omitempty"` + Addr *string `json:"address,validate:required"` + Composite_signatures *[]shared.CompositeSignature `json:"compositeSignatures,validate:required"` + Timestamp *string `json:"timestamp,validate:required"` + Profile_image *string `json:"profileImage,omitempty"` + Name *string `json:"name,omitempty"` + Website *string `json:"website,omitempty"` + Bio *string `json:"bio,omitempty"` + Twitter *string `json:"twitter,omitempty"` + Discord *string `json:"discord,omitempty"` + Instagram *string `json:"instagram,omitempty"` } func (u *User) CreateUser(db *shared.Database, payload *User) error { diff --git a/backend/main/server/controllers.go b/backend/main/server/controllers.go index 401256731..a1e93eb33 100644 --- a/backend/main/server/controllers.go +++ b/backend/main/server/controllers.go @@ -924,6 +924,16 @@ func (a *App) createUser(w http.ResponseWriter, r *http.Request) { return } + if err := helpers.validateUser( + *payload.Addr, + *payload.Timestamp, + payload.Composite_signatures, + ); err != nil { + log.Error().Err(err).Msg("Error validating signature") + respondWithError(w, errIncompleteRequest) + return + } + var user models.User if err := user.CreateUser(a.DB, &payload); err != nil { log.Error().Err(err).Msg("Error creating user") diff --git a/backend/main/server/helpers.go b/backend/main/server/helpers.go index 5ff784e40..d6d8ac00f 100644 --- a/backend/main/server/helpers.go +++ b/backend/main/server/helpers.go @@ -1128,7 +1128,6 @@ func (h *Helpers) validateTimestamp(timestamp string, expiry int) error { } func (h *Helpers) validateUser(addr, timestamp string, compositeSignatures *[]shared.CompositeSignature) error { - if err := h.validateTimestamp(timestamp, 60); err != nil { return err } diff --git a/backend/migrations/000044_create_user_table.up.sql b/backend/migrations/000044_create_user_table.up.sql index 27732969f..7fb3cf070 100644 --- a/backend/migrations/000044_create_user_table.up.sql +++ b/backend/migrations/000044_create_user_table.up.sql @@ -1,6 +1,6 @@ CREATE TABLE users ( uuid UUID PRIMARY KEY NOT NULL, - addr VARCHAR(255), + addr VARCHAR(255) NOT NULL, profile_image TEXT, name VARCHAR(50), website VARCHAR(50), diff --git a/backend/tests/test_utils/user_utils.go b/backend/tests/test_utils/user_utils.go index 902676b71..4d751d77c 100644 --- a/backend/tests/test_utils/user_utils.go +++ b/backend/tests/test_utils/user_utils.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "fmt" + "time" "net/http" "net/http/httptest" @@ -27,6 +28,18 @@ func (otu *OverflowTestUtils) CreateUserAPI(user *models.User) *httptest.Respons return otu.ExecuteRequest(req) } +func (otu *OverflowTestUtils) GetUserAPI(user *models.User) *httptest.ResponseRecorder { + req, _ := http.NewRequest("GET", fmt.Sprintf("/user/%s", *user.Addr), nil) + return otu.ExecuteRequest(req) +} + +func (otu *OverflowTestUtils) UpdateUserAPI(user *models.User) *httptest.ResponseRecorder { + json, _ := json.Marshal(user) + req, _ := http.NewRequest("PUT", "/user", bytes.NewBuffer(json)) + req.Header.Set("Content-Type", "application/json") + return otu.ExecuteRequest(req) +} + func (otu *OverflowTestUtils) GenerateUserStruct(signer string) *models.User { account, _ := otu.O.State.Accounts().ByName(fmt.Sprintf("emulator-%s", signer)) address := fmt.Sprintf("0x%s", account.Address().String()) @@ -42,6 +55,18 @@ func (otu *OverflowTestUtils) GenerateUserStruct(signer string) *models.User { } } +func (otu *OverflowTestUtils) GenerateUserPayload(signer string, user models.User) *models.User { + payload := user + fmt.Printf("payload: %+v \n", payload) + timestamp := fmt.Sprint(time.Now().UnixNano() / int64(time.Millisecond)) + fmt.Printf("timestamp: %s \n", timestamp) + compositeSignatures := otu.GenerateCompositeSignatures(signer, timestamp) + fmt.Printf("compositeSignatures: %+v \n", compositeSignatures) + payload.Timestamp = ×tamp + payload.Composite_signatures = compositeSignatures + return &payload +} + func (otu *OverflowTestUtils) GenerateFailUserStruct() *models.User { nonExistentAccount := "0x696969" diff --git a/backend/tests/user_test.go b/backend/tests/user_test.go index 5a3c14869..ebeba2046 100644 --- a/backend/tests/user_test.go +++ b/backend/tests/user_test.go @@ -16,7 +16,9 @@ func TestUser(t *testing.T) { t.Run("should be able to create a user", func(t *testing.T) { userStruct := otu.GenerateUserStruct("account") - response := otu.CreateUserAPI(userStruct) + payload := otu.GenerateUserPayload("account", *userStruct) + fmt.Printf("payload: %+v \r", payload) + response := otu.CreateUserAPI(payload) checkResponseCode(t, http.StatusCreated, response.Code) var created models.User @@ -27,8 +29,8 @@ func TestUser(t *testing.T) { t.Run("should be able to get a user", func(t *testing.T) { userStruct := otu.GenerateUserStruct("account") - response := otu.CreateUserAPI(userStruct) - checkResponseCode(t, http.StatusCreated, response.Code) + response := otu.GetUserAPI(userStruct) + checkResponseCode(t, http.StatusOK, response.Code) var created models.User json.Unmarshal(response.Body.Bytes(), &created) @@ -44,7 +46,8 @@ func TestUser(t *testing.T) { t.Run("should be able to update a user", func(t *testing.T) { userStruct := otu.GenerateUserStruct("account") - response := otu.CreateUserAPI(userStruct) + payload := otu.GenerateUserPayload("account", *userStruct) + response := otu.CreateUserAPI(payload) checkResponseCode(t, http.StatusCreated, response.Code) var toUpdate models.User @@ -53,10 +56,8 @@ func TestUser(t *testing.T) { updatedName := "Updated Name" toUpdate.Name = &updatedName - payload, _ := json.Marshal(toUpdate) - req, _ := http.NewRequest("PUT", fmt.Sprintf("/user"), bytes.NewBuffer(payload)) - req.Header.Set("Content-Type", "application/json") - response = otu.ExecuteRequest(req) + updatePayload := otu.GenerateUserPayload("account", toUpdate) + response = otu.UpdateUserAPI(updatePayload) var updated models.User json.Unmarshal(response.Body.Bytes(), &updated) From b358a3a1d5bdc0b5e0128aa36ac6461f1fe6ecbe Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Thu, 27 Oct 2022 15:48:50 +0100 Subject: [PATCH 08/12] added created_at sql func and column --- backend/migrations/000044_create_user_table.up.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/migrations/000044_create_user_table.up.sql b/backend/migrations/000044_create_user_table.up.sql index 7fb3cf070..a614229bf 100644 --- a/backend/migrations/000044_create_user_table.up.sql +++ b/backend/migrations/000044_create_user_table.up.sql @@ -1,6 +1,7 @@ CREATE TABLE users ( uuid UUID PRIMARY KEY NOT NULL, addr VARCHAR(255) NOT NULL, + created_at TIMESTAMP without time zone default (now() at time zone 'utc'), profile_image TEXT, name VARCHAR(50), website VARCHAR(50), From d429fd369bd0771ad6e2554c5a352bf4fd1d46a9 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Mon, 31 Oct 2022 16:04:09 +0000 Subject: [PATCH 09/12] removed logs --- backend/tests/test_utils/user_utils.go | 3 --- backend/tests/user_test.go | 1 - 2 files changed, 4 deletions(-) diff --git a/backend/tests/test_utils/user_utils.go b/backend/tests/test_utils/user_utils.go index 4d751d77c..e113a9853 100644 --- a/backend/tests/test_utils/user_utils.go +++ b/backend/tests/test_utils/user_utils.go @@ -57,11 +57,8 @@ func (otu *OverflowTestUtils) GenerateUserStruct(signer string) *models.User { func (otu *OverflowTestUtils) GenerateUserPayload(signer string, user models.User) *models.User { payload := user - fmt.Printf("payload: %+v \n", payload) timestamp := fmt.Sprint(time.Now().UnixNano() / int64(time.Millisecond)) - fmt.Printf("timestamp: %s \n", timestamp) compositeSignatures := otu.GenerateCompositeSignatures(signer, timestamp) - fmt.Printf("compositeSignatures: %+v \n", compositeSignatures) payload.Timestamp = ×tamp payload.Composite_signatures = compositeSignatures return &payload diff --git a/backend/tests/user_test.go b/backend/tests/user_test.go index ebeba2046..ba1bedab3 100644 --- a/backend/tests/user_test.go +++ b/backend/tests/user_test.go @@ -17,7 +17,6 @@ func TestUser(t *testing.T) { t.Run("should be able to create a user", func(t *testing.T) { userStruct := otu.GenerateUserStruct("account") payload := otu.GenerateUserPayload("account", *userStruct) - fmt.Printf("payload: %+v \r", payload) response := otu.CreateUserAPI(payload) checkResponseCode(t, http.StatusCreated, response.Code) From b6dbb0fd3f7ed38a05774a3e171d279a3e5036b4 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 1 Nov 2022 13:31:05 +0000 Subject: [PATCH 10/12] updated getCommunities endpoint --- backend/main/models/community.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/backend/main/models/community.go b/backend/main/models/community.go index 96db53593..df0fb6728 100644 --- a/backend/main/models/community.go +++ b/backend/main/models/community.go @@ -47,6 +47,7 @@ type Community struct { Timestamp string `json:"timestamp" validate:"required"` Composite_signatures *[]s.CompositeSignature `json:"compositeSignatures"` Creator_addr string `json:"creatorAddr" validate:"required"` + Creator_image *string `json:"creatorImage,omitempty"` Signing_addr *string `json:"signingAddr,omitempty"` Voucher *shared.Voucher `json:"voucher,omitempty"` Created_at *time.Time `json:"createdAt,omitempty"` @@ -96,7 +97,7 @@ type CanUserCreateProposalResponse struct { IsAuthor bool `json:"isAuthor"` HasPermission bool `json:"hasPermission"` Reason string `json:"reason,omitempty"` - Contract_type string `json:"contractType,omitempty"` + Contract_type string `json:"contractType,omitempty"` Error error `json:"error,omitempty"` } @@ -201,8 +202,10 @@ func GetCommunityTypes(db *s.Database) ([]*CommunityType, error) { func (c *Community) GetCommunity(db *s.Database) error { return pgxscan.Get(db.Context, db.Conn, c, - `SELECT * from communities WHERE id = $1`, - c.ID) + `SELECT communities.*, users.profile_image AS creator_image + FROM communities + JOIN users on users.addr = communities.creator_addr + WHERE id = $1`, c.ID) } func GetCommunities(db *s.Database, pageParams shared.PageParams) ([]*Community, int, error) { From bb8b40e660398bacc96755a975806a9434bffcda Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 1 Nov 2022 13:56:48 +0000 Subject: [PATCH 11/12] updated community and proposal endpoints --- backend/main/models/community.go | 4 +++- backend/main/models/proposal.go | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/backend/main/models/community.go b/backend/main/models/community.go index df0fb6728..ac253a37c 100644 --- a/backend/main/models/community.go +++ b/backend/main/models/community.go @@ -212,7 +212,9 @@ func GetCommunities(db *s.Database, pageParams shared.PageParams) ([]*Community, var communities []*Community err := pgxscan.Select(db.Context, db.Conn, &communities, ` - SELECT * FROM communities + SELECT communities.*, users.profile_image AS creator_image + FROM communities + JOIN users on users.addr = communities.creator_addr LIMIT $1 OFFSET $2 `, pageParams.Count, pageParams.Start) diff --git a/backend/main/models/proposal.go b/backend/main/models/proposal.go index c1347370b..cc0aea3ee 100644 --- a/backend/main/models/proposal.go +++ b/backend/main/models/proposal.go @@ -26,6 +26,7 @@ type Proposal struct { Max_weight *float64 `json:"maxWeight,omitempty"` Min_balance *float64 `json:"minBalance,omitempty"` Creator_addr string `json:"creatorAddr" validate:"required"` + Creator_image *string `json:"creatorImage,omitempty"` Start_time time.Time `json:"startTime" validate:"required"` Result *string `json:"result,omitempty"` End_time time.Time `json:"endTime" validate:"required"` @@ -114,10 +115,12 @@ func GetProposalsForCommunity( func (p *Proposal) GetProposalById(db *s.Database) error { sql := ` - SELECT p.*, %s, count(v.id) as total_votes from proposals as p + SELECT p.*, %s, u.profile_image as creator_image, + COUNT(v.id) as total_votes from proposals as p left join votes as v on v.proposal_id = p.id + left join users as u on u.addr = p.creator_addr WHERE p.id = $1 - GROUP BY p.id` + GROUP BY p.id, u.profile_image` sql = fmt.Sprintf(sql, computedStatusSQL) return pgxscan.Get(db.Context, db.Conn, p, sql, p.ID) } From 8d95046591179ba11929b57f9573a18ace516e24 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 1 Nov 2022 14:22:07 +0000 Subject: [PATCH 12/12] updated getProposalsForCommunity endpoint --- backend/main/models/proposal.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/backend/main/models/proposal.go b/backend/main/models/proposal.go index cc0aea3ee..4f4b0aa0c 100644 --- a/backend/main/models/proposal.go +++ b/backend/main/models/proposal.go @@ -88,7 +88,14 @@ func GetProposalsForCommunity( var err error // Get Proposals - sql := fmt.Sprintf(`SELECT *, %s FROM proposals WHERE community_id = $3`, computedStatusSQL) + sql := fmt.Sprintf( + `SELECT p.*, u.profile_image as creator_image, %s + FROM proposals as p + left join users as u on u.addr = p.creator_addr + WHERE community_id = $3 + `, + computedStatusSQL, + ) statusesFilterSql := generateStatusesFilterSQL(statuses) orderBySql := fmt.Sprintf(` ORDER BY created_at %s`, params.Order)