diff --git a/packages/restapi/src/lib/chat/createGroup.ts b/packages/restapi/src/lib/chat/createGroup.ts index 3632603e5..1f960e34b 100644 --- a/packages/restapi/src/lib/chat/createGroup.ts +++ b/packages/restapi/src/lib/chat/createGroup.ts @@ -63,7 +63,7 @@ export const createGroupCore = async ( groupType, scheduleAt, scheduleEnd, - rules + rules, } = options || {}; try { @@ -71,7 +71,7 @@ export const createGroupCore = async ( throw new Error(`At least one from account or signer is necessary!`); } - validateScheduleDates(scheduleAt, scheduleEnd) + validateScheduleDates(scheduleAt, scheduleEnd); const wallet = getWallet({ account, signer }); diff --git a/packages/restapi/src/lib/chat/helpers/validator.ts b/packages/restapi/src/lib/chat/helpers/validator.ts index 6f21839cd..5c241a7c3 100644 --- a/packages/restapi/src/lib/chat/helpers/validator.ts +++ b/packages/restapi/src/lib/chat/helpers/validator.ts @@ -1,7 +1,4 @@ -import { - isValidETHAddress, - isValidNFTCAIP10Address, -} from '../../helpers'; +import { isValidETHAddress, isValidNFTCAIP10Address } from '../../helpers'; export const createGroupRequestValidator = ( groupName: string, @@ -71,132 +68,143 @@ export const createGroupRequestValidator = ( }; export const createSpaceRequestValidator = ( - spaceName: string, spaceDescription: string | null,members: Array < string > , admins: Array < string > , contractAddressNFT ? : string, - numberOfNFTs ? : number, - contractAddressERC20 ? : string, - numberOfERC20 ? : number + spaceName: string, + spaceDescription: string | null, + members: Array, + admins: Array, + contractAddressNFT?: string, + numberOfNFTs?: number, + contractAddressERC20?: string, + numberOfERC20?: number ): void => { + if (spaceName == null || spaceName.length == 0) { + throw new Error(`spaceName cannot be null or empty`); + } - if (spaceName == null || spaceName.length == 0) { - throw new Error(`spaceName cannot be null or empty`); - } - - if (spaceName.length > 50) { - throw new Error(`groupName cannot be more than 50 characters`); - } + if (spaceName.length > 50) { + throw new Error(`groupName cannot be more than 50 characters`); + } - if (spaceDescription && spaceDescription.length > 150) { - throw new Error(`spaceDescription cannot be more than 150 characters`); - } + if (spaceDescription && spaceDescription.length > 150) { + throw new Error(`spaceDescription cannot be more than 150 characters`); + } - if (members == null) { - throw new Error(`members cannot be null`); - } + if (members == null) { + throw new Error(`members cannot be null`); + } - for (let i = 0; i < members.length; i++) { - if (members[i] && !isValidETHAddress(members[i])) { - throw new Error(`Invalid member address!`); - } + for (let i = 0; i < members.length; i++) { + if (members[i] && !isValidETHAddress(members[i])) { + throw new Error(`Invalid member address!`); } + } - if (admins == null) { - throw new Error(`admins cannot be null`); - } + if (admins == null) { + throw new Error(`admins cannot be null`); + } - for (let i = 0; i < admins.length; i++) { - if (!isValidETHAddress(admins[i])) { - throw new Error(`Invalid admin address!`); - } + for (let i = 0; i < admins.length; i++) { + if (!isValidETHAddress(admins[i])) { + throw new Error(`Invalid admin address!`); } + } - if (contractAddressNFT != null && contractAddressNFT?.length > 0 && !isValidNFTCAIP10Address(contractAddressNFT)) { - throw new Error(`Invalid contractAddressNFT address!`); - } + if ( + contractAddressNFT != null && + contractAddressNFT?.length > 0 && + !isValidNFTCAIP10Address(contractAddressNFT) + ) { + throw new Error(`Invalid contractAddressNFT address!`); + } - if (numberOfNFTs != null && numberOfNFTs < 0) { - throw new Error(`numberOfNFTs cannot be negative number`); - } + if (numberOfNFTs != null && numberOfNFTs < 0) { + throw new Error(`numberOfNFTs cannot be negative number`); + } - if (contractAddressERC20 != null && contractAddressERC20?.length > 0 && !isValidNFTCAIP10Address(contractAddressERC20)) { - throw new Error(`Invalid contractAddressERC20 address!`); - } + if ( + contractAddressERC20 != null && + contractAddressERC20?.length > 0 && + !isValidNFTCAIP10Address(contractAddressERC20) + ) { + throw new Error(`Invalid contractAddressERC20 address!`); + } - if (numberOfERC20 != null && numberOfERC20 < 0) { - throw new Error(`numberOfERC20 cannot be negative number`); - } + if (numberOfERC20 != null && numberOfERC20 < 0) { + throw new Error(`numberOfERC20 cannot be negative number`); + } }; -export const validateScheduleDates = (scheduleAt?: Date | null, - scheduleEnd ? : Date | null): void => { - - if (scheduleAt) { - const start = new Date(scheduleAt) - const now = new Date() +export const validateScheduleDates = ( + scheduleAt?: Date | null, + scheduleEnd?: Date | null +): void => { + if (scheduleAt) { + const start = new Date(scheduleAt); + const now = new Date(); - if (start < now) { - throw new Error('Schedule start time must be in the future.') - } + if (start < now) { + throw new Error('Schedule start time must be in the future.'); + } - if (scheduleEnd) { - const end = new Date(scheduleEnd) + if (scheduleEnd) { + const end = new Date(scheduleEnd); - if (end < now) { - throw new Error('Schedule end time must be in the future.') - } + if (end < now) { + throw new Error('Schedule end time must be in the future.'); + } - if (start >= end) { - throw new Error('Schedule start time must be earlier than end time.') - } - } + if (start >= end) { + throw new Error('Schedule start time must be earlier than end time.'); + } } + } }; export const updateGroupRequestValidator = ( - chatId: string, - groupName: string, - members: Array < string > , - admins: Array < string > , - address: string, - groupDescription?: string | null + chatId: string, + groupName: string, + members: Array, + admins: Array, + address: string, + groupDescription?: string | null ): void => { + if (chatId == null || chatId.length == 0) { + throw new Error(`chatId cannot be null or empty`); + } + if (groupName == null || groupName.length == 0) { + throw new Error(`groupName cannot be null or empty`); + } - if (chatId == null || chatId.length == 0) { - throw new Error(`chatId cannot be null or empty`); - } - - if (groupName == null || groupName.length == 0) { - throw new Error(`groupName cannot be null or empty`); - } - - if (groupName != null && groupName.length > 50) { - throw new Error(`groupName cannot be more than 50 characters`); - } + if (groupName != null && groupName.length > 50) { + throw new Error(`groupName cannot be more than 50 characters`); + } - if ( - groupDescription && groupDescription != null && - groupDescription.length > 150 - ) { - throw new Error(`groupDescription cannot be more than 150 characters`); - } + if ( + groupDescription && + groupDescription != null && + groupDescription.length > 150 + ) { + throw new Error(`groupDescription cannot be more than 150 characters`); + } - if (members != null && members.length > 0) { - for (let i = 0; i < members.length; i++) { - if (!isValidETHAddress(members[i])) { - throw new Error(`Invalid member address in members list!`); - } - } + if (members != null && members.length > 0) { + for (let i = 0; i < members.length; i++) { + if (!isValidETHAddress(members[i])) { + throw new Error(`Invalid member address in members list!`); + } } + } - if (admins != null && admins.length > 0) { - for (let i = 0; i < admins.length; i++) { - if (!isValidETHAddress(admins[i])) { - throw new Error(`Invalid member address in admins list!`); - } - } + if (admins != null && admins.length > 0) { + for (let i = 0; i < admins.length; i++) { + if (!isValidETHAddress(admins[i])) { + throw new Error(`Invalid member address in admins list!`); + } } + } - if (address != null && !isValidETHAddress(address)) { - throw new Error(`Invalid address field!`); - } -}; \ No newline at end of file + if (address != null && !isValidETHAddress(address)) { + throw new Error(`Invalid address field!`); + } +}; diff --git a/packages/restapi/src/lib/chat/updateGroupProfile.ts b/packages/restapi/src/lib/chat/updateGroupProfile.ts index 370fc580e..189413c25 100644 --- a/packages/restapi/src/lib/chat/updateGroupProfile.ts +++ b/packages/restapi/src/lib/chat/updateGroupProfile.ts @@ -9,19 +9,14 @@ import { SignerType, } from '../types'; import { - IUpdateGroupRequestPayload, - updateGroupPayload, sign, getWallet, getAccountAddress, getConnectedUserV2, updateGroupRequestValidator, - getAdminsList, - getMembersList, } from './helpers'; - -import { getGroup } from './getGroup'; import * as CryptoJS from 'crypto-js'; +import { getGroup } from './getGroup'; export interface ChatUpdateGroupProfileType extends EnvOptionsType { account?: string | null; @@ -34,9 +29,6 @@ export interface ChatUpdateGroupProfileType extends EnvOptionsType { scheduleAt?: Date | null; scheduleEnd?: Date | null; status?: ChatStatus | null; - // If meta is not passed, old meta is not affected - // If passed as null will update to null - // If passed as string will update to that value meta?: string | null; rules?: Rules | null; } @@ -63,6 +55,9 @@ export const updateGroupProfile = async ( rules, } = options || {}; try { + /** + * VALIDATIONS + */ if (account == null && signer == null) { throw new Error(`At least one from account or signer is necessary!`); } @@ -78,54 +73,52 @@ export const updateGroupProfile = async ( groupDescription ); - const connectedUser = await getConnectedUserV2(wallet, pgpPrivateKey, env); - const group = await getGroup({ - chatId: chatId, - env: env, + chatId, + env, }); - // TODO: look at user did in updateGroup - const convertedMembers = getMembersList( - group.members, - group.pendingMembers - ); - - const convertedAdmins = getAdminsList(group.members, group.pendingMembers); - + /** + * CREATE PROFILE VERIFICATION PROOF + */ const bodyToBeHashed = { + chatId: chatId, groupName: groupName, groupDescription: groupDescription, groupImage: groupImage, - members: convertedMembers, - admins: convertedAdmins, - chatId: chatId, + meta: meta, + scheduleAt: scheduleAt, + scheduleEnd: scheduleEnd, + rules: rules, + status: status, + isPublic: group.isPublic, + groupType: group.groupType, }; + const hash = CryptoJS.SHA256(JSON.stringify(bodyToBeHashed)).toString(); + const connectedUser = await getConnectedUserV2(wallet, pgpPrivateKey, env); const signature: string = await sign({ message: hash, signingKey: connectedUser.privateKey!, }); - const sigType = 'pgp'; - const verificationProof: string = sigType + ':' + signature + ':' + account; + const sigType = 'pgpv2'; + // Account is need to verify the signature at any future point + const verificationProof: string = + sigType + ':' + signature + ':' + connectedUser.did; + + /** + * API CALL TO PUSH NODES + */ const API_BASE_URL = getAPIBaseUrls(env); - const apiEndpoint = `${API_BASE_URL}/v1/chat/groups/${chatId}`; - const body: IUpdateGroupRequestPayload = updateGroupPayload( - groupName, - convertedMembers, - convertedAdmins, - connectedUser.did, - verificationProof, - null, - null, - groupDescription, - groupImage, - scheduleAt, - scheduleEnd, - status, - meta, - rules - ); + const apiEndpoint = `${API_BASE_URL}/v1/chat/groups/${chatId}/profile`; + + const { + chatId: chat_id, + isPublic: is_public, + groupType: group_type, + ...body + } = bodyToBeHashed; + (body as any).verificationProof = verificationProof; return axios .put(apiEndpoint, body) diff --git a/packages/restapi/src/lib/pushapi/chat.ts b/packages/restapi/src/lib/pushapi/chat.ts index 193db597a..8a9077bf8 100644 --- a/packages/restapi/src/lib/pushapi/chat.ts +++ b/packages/restapi/src/lib/pushapi/chat.ts @@ -282,17 +282,17 @@ export class Chat { const updateGroupProfileOptions: ChatUpdateGroupProfileType = { chatId: chatId, groupName: options.name ? options.name : group.groupName, - groupImage: options.image ? options.image : group.groupImage, groupDescription: options.description ? options.description : group.groupDescription, + groupImage: options.image ? options.image : group.groupImage, + meta: options.meta ? options.meta : group.meta, scheduleAt: options.scheduleAt ? options.scheduleAt : group.scheduleAt, scheduleEnd: options.scheduleEnd ? options.scheduleEnd : group.scheduleEnd, - status: options.status ? options.status : group.status, - meta: options.meta ? options.meta : group.meta, rules: options.rules ? options.rules : group.rules, + status: options.status ? options.status : group.status, account: this.account, pgpPrivateKey: this.decryptedPgpPvtKey, env: this.env, diff --git a/packages/restapi/tests/lib/pushapi/chat.group.test.ts b/packages/restapi/tests/lib/pushapi/chat.group.test.ts new file mode 100644 index 000000000..6b733faf0 --- /dev/null +++ b/packages/restapi/tests/lib/pushapi/chat.group.test.ts @@ -0,0 +1,70 @@ +import * as path from 'path'; +import * as dotenv from 'dotenv'; +dotenv.config({ path: path.resolve(__dirname, '../../.env') }); + +import { PushAPI } from '../../../src/lib/pushapi/PushAPI'; // Ensure correct import path +import { expect } from 'chai'; +import { ethers } from 'ethers'; +import { ENV } from '../../../src/lib/constants'; + +const env: ENV = ENV.DEV; +describe('PushAPI.chat.group functionality', () => { + let userAlice: PushAPI; + let userBob: PushAPI; + let signer1: any; + let account1: string; + let signer2: any; + let account2: string; + const MESSAGE = 'Hey There!!!'; + + beforeEach(async () => { + const WALLET1 = ethers.Wallet.createRandom(); + signer1 = new ethers.Wallet(WALLET1.privateKey); + account1 = WALLET1.address; + + const WALLET2 = ethers.Wallet.createRandom(); + signer2 = new ethers.Wallet(WALLET2.privateKey); + account2 = WALLET2.address; + + userAlice = await PushAPI.initialize(signer1, { env }); + userBob = await PushAPI.initialize(signer2, { env }); + }); + + it('Should thorw error on update Group By Non-Admin', async () => { + const createdGroup = await userAlice.chat.group.create('Test Grp', { + description: 'Test Desc', + image: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAvklEQVR4AcXBsW2FMBiF0Y8r3GQb6jeBxRauYRpo4yGQkMd4A7kg7Z/GUfSKe8703fKDkTATZsJsrr0RlZSJ9r4RLayMvLmJjnQS1d6IhJkwE2bT13U/DBzp5BN73xgRZsJMmM1HOolqb/yWiWpvjJSUiRZWopIykTATZsJs5g+1N6KSMiO1N/5DmAkzYTa9Lh6MhJkwE2ZzSZlo7xvRwson3txERzqJhJkwE2bT6+JhoKTMJ2pvjAgzYSbMfgDlXixqjH6gRgAAAABJRU5ErkJggg==', + members: [account2], + admins: [], + private: false, + }); + + await expect( + userBob.chat.group.update(createdGroup.chatId, { + description: 'Updated Test Grp Description', + }) + ).to.be.rejectedWith(`Address eip155:${account2} is not a group admin`); + }); + + it('Should update Group By Admin', async () => { + const createdGroup = await userAlice.chat.group.create('Test Grp', { + description: 'Test Desc', + image: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAvklEQVR4AcXBsW2FMBiF0Y8r3GQb6jeBxRauYRpo4yGQkMd4A7kg7Z/GUfSKe8703fKDkTATZsJsrr0RlZSJ9r4RLayMvLmJjnQS1d6IhJkwE2bT13U/DBzp5BN73xgRZsJMmM1HOolqb/yWiWpvjJSUiRZWopIykTATZsJs5g+1N6KSMiO1N/5DmAkzYTa9Lh6MhJkwE2ZzSZlo7xvRwson3txERzqJhJkwE2bT6+JhoKTMJ2pvjAgzYSbMfgDlXixqjH6gRgAAAABJRU5ErkJggg==', + members: [account2], + admins: [], + private: false, + }); + + const updatedGroup = await userAlice.chat.group.update( + createdGroup.chatId, + { + description: 'Updated Test Grp Description', + } + ); + expect(updatedGroup.groupDescription).to.equal( + 'Updated Test Grp Description' + ); + }); +});