From 6403a7413bd1c560d1beada7b4af1698ddc570d8 Mon Sep 17 00:00:00 2001 From: Sale Djenic Date: Thu, 25 Jan 2024 14:29:29 +0100 Subject: [PATCH] feat: new endpoint added which returns accounts of all contacts that match the passed address --- protocol/contact.go | 7 +- protocol/messenger_profile_showcase.go | 4 + protocol/persistence_profile_showcase.go | 67 ++++++--- protocol/persistence_profile_showcase_test.go | 136 ++++++++++++++++-- services/ext/api.go | 5 + 5 files changed, 189 insertions(+), 30 deletions(-) diff --git a/protocol/contact.go b/protocol/contact.go index d9c166d0a..beff45f2b 100644 --- a/protocol/contact.go +++ b/protocol/contact.go @@ -380,9 +380,12 @@ func buildContact(publicKeyString string, publicKey *ecdsa.PublicKey) (*Contact, return nil, err } + address := crypto.PubkeyToAddress(*publicKey) + contact := &Contact{ - ID: publicKeyString, - Alias: getShortenedCompressedKey(compressedKey), + ID: publicKeyString, + Alias: getShortenedCompressedKey(compressedKey), + Address: types.EncodeHex(address[:]), } return contact, nil diff --git a/protocol/messenger_profile_showcase.go b/protocol/messenger_profile_showcase.go index a9d6fb631..2179b6f60 100644 --- a/protocol/messenger_profile_showcase.go +++ b/protocol/messenger_profile_showcase.go @@ -217,6 +217,10 @@ func (m *Messenger) GetProfileShowcaseForContact(contactID string) (*ProfileShow return m.persistence.GetProfileShowcaseForContact(contactID) } +func (m *Messenger) GetProfileShowcaseAccountsByAddress(address string) ([]*ProfileShowcaseAccount, error) { + return m.persistence.GetProfileShowcaseAccountsByAddress(address) +} + func (m *Messenger) EncryptProfileShowcaseEntriesWithContactPubKeys(entries *protobuf.ProfileShowcaseEntries, contacts []*Contact) (*protobuf.ProfileShowcaseEntriesEncrypted, error) { // Make AES key AESKey := make([]byte, 32) diff --git a/protocol/persistence_profile_showcase.go b/protocol/persistence_profile_showcase.go index 2539d1a83..b4db00b4d 100644 --- a/protocol/persistence_profile_showcase.go +++ b/protocol/persistence_profile_showcase.go @@ -3,6 +3,7 @@ package protocol import ( "context" "database/sql" + "errors" ) type ProfileShowcaseVisibility int @@ -34,8 +35,8 @@ const selectContactProfileShowcaseCommunityQuery = "SELECT community_id, sort_or const removeContactProfileShowcaseCommunityQuery = "DELETE FROM profile_showcase_communities_contacts WHERE contact_id = ?" // #nosec G101 const upsertContactProfileShowcaseAccountQuery = "INSERT OR REPLACE INTO profile_showcase_accounts_contacts(contact_id, address, name, color_id, emoji, sort_order) VALUES (?, ?, ?, ?, ?, ?)" // #nosec G101 -const selectContactProfileShowcaseAccountQuery = "SELECT address, name, color_id, emoji, sort_order FROM profile_showcase_accounts_contacts WHERE contact_id = ?" // #nosec G101 -const removeContactProfileShowcaseAccountQuery = "DELETE FROM profile_showcase_accounts_contacts WHERE contact_id = ?" +const selectContactProfileShowcaseAccountQuery = "SELECT * FROM profile_showcase_accounts_contacts WHERE contact_id = ?" // #nosec G101 +const removeContactProfileShowcaseAccountQuery = "DELETE FROM profile_showcase_accounts_contacts WHERE contact_id = ?" // #nosec G101 const upsertContactProfileShowcaseCollectibleQuery = "INSERT OR REPLACE INTO profile_showcase_collectibles_contacts(contact_id, contract_address, chain_id, token_id, community_id, account_address, sort_order) VALUES (?, ?, ?, ?, ?, ?, ?)" // #nosec G101 const selectContactProfileShowcaseCollectibleQuery = "SELECT contract_address, chain_id, token_id, community_id, account_address, sort_order FROM profile_showcase_collectibles_contacts WHERE contact_id = ?" // #nosec G101 @@ -49,6 +50,18 @@ const upsertContactProfileShowcaseUnverifiedTokenQuery = "INSERT OR REPLACE INTO const selectContactProfileShowcaseUnverifiedTokenQuery = "SELECT contract_address, chain_id, community_id, sort_order FROM profile_showcase_unverified_tokens_contacts WHERE contact_id = ?" // #nosec G101 const removeContactProfileShowcaseUnverifiedTokenQuery = "DELETE FROM profile_showcase_unverified_tokens_contacts WHERE contact_id = ?" // #nosec G101 +const selectProfileShowcaseAccountsWhichMatchTheAddress = ` +SELECT psa.* +FROM + contacts c +LEFT JOIN + profile_showcase_accounts_contacts psa +ON + c.id = psa.contact_id +WHERE + psa.address = ? +` + type ProfileShowcaseCommunityPreference struct { CommunityID string `json:"communityId"` ShowcaseVisibility ProfileShowcaseVisibility `json:"showcaseVisibility"` @@ -102,11 +115,12 @@ type ProfileShowcaseCommunity struct { } type ProfileShowcaseAccount struct { - Address string `json:"address"` - Name string `json:"name"` - ColorID string `json:"colorId"` - Emoji string `json:"emoji"` - Order int `json:"order"` + ContactID string `json:"contactId"` + Address string `json:"address"` + Name string `json:"name"` + ColorID string `json:"colorId"` + Emoji string `json:"emoji"` + Order int `json:"order"` } type ProfileShowcaseCollectible struct { @@ -392,25 +406,42 @@ func (db sqlitePersistence) saveProfileShowcaseAccountContact(tx *sql.Tx, contac return err } +func (db sqlitePersistence) processProfileShowcaseAccounts(rows *sql.Rows) (result []*ProfileShowcaseAccount, err error) { + if rows == nil { + return nil, errors.New("rows is nil") + } + + for rows.Next() { + account := &ProfileShowcaseAccount{} + + err = rows.Scan(&account.Address, &account.Name, &account.ColorID, &account.Emoji, &account.Order, &account.ContactID) + if err != nil { + return + } + + result = append(result, account) + } + + err = rows.Err() + return +} + func (db sqlitePersistence) getProfileShowcaseAccountsContact(tx *sql.Tx, contactID string) ([]*ProfileShowcaseAccount, error) { rows, err := tx.Query(selectContactProfileShowcaseAccountQuery, contactID) if err != nil { return nil, err } - accounts := []*ProfileShowcaseAccount{} + return db.processProfileShowcaseAccounts(rows) +} - for rows.Next() { - account := &ProfileShowcaseAccount{} - - err := rows.Scan(&account.Address, &account.Name, &account.ColorID, &account.Emoji, &account.Order) - if err != nil { - return nil, err - } - - accounts = append(accounts, account) +func (db sqlitePersistence) GetProfileShowcaseAccountsByAddress(address string) ([]*ProfileShowcaseAccount, error) { + rows, err := db.db.Query(selectProfileShowcaseAccountsWhichMatchTheAddress, address) + if err != nil { + return nil, err } - return accounts, nil + + return db.processProfileShowcaseAccounts(rows) } func (db sqlitePersistence) clearProfileShowcaseAccountsContact(tx *sql.Tx, contactID string) error { diff --git a/protocol/persistence_profile_showcase_test.go b/protocol/persistence_profile_showcase_test.go index 89727d58a..a43b5c07c 100644 --- a/protocol/persistence_profile_showcase_test.go +++ b/protocol/persistence_profile_showcase_test.go @@ -142,18 +142,20 @@ func (s *TestProfileShowcasePersistence) TestProfileShowcaseContacts() { }, Accounts: []*ProfileShowcaseAccount{ &ProfileShowcaseAccount{ - Address: "0x32433445133424", - Name: "Status Account", - ColorID: "blue", - Emoji: "-_-", - Order: 0, + ContactID: "contact_1", + Address: "0x32433445133424", + Name: "Status Account", + ColorID: "blue", + Emoji: "-_-", + Order: 0, }, &ProfileShowcaseAccount{ - Address: "0x3845354643324", - Name: "Money Box", - ColorID: "red", - Emoji: ":o)", - Order: 1, + ContactID: "contact_1", + Address: "0x3845354643324", + Name: "Money Box", + ColorID: "red", + Emoji: ":o)", + Order: 1, }, }, Collectibles: []*ProfileShowcaseCollectible{ @@ -258,3 +260,117 @@ func (s *TestProfileShowcasePersistence) TestProfileShowcaseContacts() { s.Require().Equal(0, len(showcase2Back.VerifiedTokens)) s.Require().Equal(0, len(showcase2Back.UnverifiedTokens)) } + +func (s *TestProfileShowcasePersistence) TestFetchingProfileShowcaseAccountsByAddress() { + db, err := openTestDB() + s.Require().NoError(err) + persistence := newSQLitePersistence(db) + + conatacts := []*Contact{ + &Contact{ + ID: "contact_1", + }, + &Contact{ + ID: "contact_2", + }, + &Contact{ + ID: "contact_3", + }, + } + + err = persistence.SaveContacts(conatacts) + s.Require().NoError(err) + + showcase1 := &ProfileShowcase{ + ContactID: "contact_1", + Accounts: []*ProfileShowcaseAccount{ + &ProfileShowcaseAccount{ + ContactID: "contact_1", + Address: "0x0000000000000000000000000000000000000001", + Name: "Contact1-Account1", + ColorID: "blue", + Emoji: "-_-", + Order: 0, + }, + &ProfileShowcaseAccount{ + ContactID: "contact_1", + Address: "0x0000000000000000000000000000000000000002", + Name: "Contact1-Account2", + ColorID: "blue", + Emoji: "-_-", + Order: 1, + }, + }, + } + showcase2 := &ProfileShowcase{ + ContactID: "contact_2", + Accounts: []*ProfileShowcaseAccount{ + &ProfileShowcaseAccount{ + ContactID: "contact_2", + Address: "0x0000000000000000000000000000000000000001", + Name: "Contact2-Account1", + ColorID: "blue", + Emoji: "-_-", + Order: 0, + }, + &ProfileShowcaseAccount{ + ContactID: "contact_2", + Address: "0x0000000000000000000000000000000000000002", + Name: "Contact2-Account2", + ColorID: "blue", + Emoji: "-_-", + Order: 1, + }, + }, + } + showcase3 := &ProfileShowcase{ + ContactID: "contact_3", + Accounts: []*ProfileShowcaseAccount{ + &ProfileShowcaseAccount{ + ContactID: "contact_3", + Address: "0x0000000000000000000000000000000000000001", + Name: "Contact3-Account1", + ColorID: "blue", + Emoji: "-_-", + Order: 0, + }, + }, + } + + err = persistence.SaveProfileShowcaseForContact(showcase1) + s.Require().NoError(err) + err = persistence.SaveProfileShowcaseForContact(showcase2) + s.Require().NoError(err) + err = persistence.SaveProfileShowcaseForContact(showcase3) + s.Require().NoError(err) + + showcaseAccounts, err := persistence.GetProfileShowcaseAccountsByAddress(showcase1.Accounts[0].Address) + s.Require().NoError(err) + + s.Require().Equal(3, len(showcaseAccounts)) + for i := 0; i < len(showcaseAccounts); i++ { + if showcaseAccounts[i].ContactID == showcase1.ContactID { + s.Require().Equal(showcase1.Accounts[0].Address, showcase1.Accounts[0].Address) + } else if showcaseAccounts[i].ContactID == showcase2.ContactID { + s.Require().Equal(showcase2.Accounts[0].Address, showcase2.Accounts[0].Address) + } else if showcaseAccounts[i].ContactID == showcase3.ContactID { + s.Require().Equal(showcase3.Accounts[0].Address, showcase3.Accounts[0].Address) + } else { + s.Require().Fail("unexpected contact id") + } + } + + showcaseAccounts, err = persistence.GetProfileShowcaseAccountsByAddress(showcase1.Accounts[1].Address) + s.Require().NoError(err) + + s.Require().Equal(2, len(showcaseAccounts)) + for i := 0; i < len(showcaseAccounts); i++ { + if showcaseAccounts[i].ContactID == showcase1.ContactID { + s.Require().Equal(showcase1.Accounts[0].Address, showcase1.Accounts[0].Address) + } else if showcaseAccounts[i].ContactID == showcase2.ContactID { + s.Require().Equal(showcase2.Accounts[0].Address, showcase2.Accounts[0].Address) + } else { + s.Require().Fail("unexpected contact id") + } + } +} diff --git a/services/ext/api.go b/services/ext/api.go index 55356804b..5a9549272 100644 --- a/services/ext/api.go +++ b/services/ext/api.go @@ -1668,6 +1668,11 @@ func (api *PublicAPI) GetProfileShowcaseForContact(contactID string) (*protocol. return api.service.messenger.GetProfileShowcaseForContact(contactID) } +// Get profile showcase accounts by address +func (api *PublicAPI) GetProfileShowcaseAccountsByAddress(address string) ([]*protocol.ProfileShowcaseAccount, error) { + return api.service.messenger.GetProfileShowcaseAccountsByAddress(address) +} + // Returns response with AC notification when owner token is received func (api *PublicAPI) RegisterOwnerTokenReceivedNotification(communityID string) (*protocol.MessengerResponse, error) { return api.service.messenger.CreateResponseWithACNotification(communityID, protocol.ActivityCenterNotificationTypeOwnerTokenReceived, false)