Skip to content

Commit

Permalink
fix: optimization fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
mohammeds1992 committed Nov 9, 2023
1 parent c4766c6 commit 809b3ee
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 15 deletions.
30 changes: 30 additions & 0 deletions packages/restapi/src/lib/chat/getAllGroupMembersPublicKeys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { getGroupMembers } from './getGroupMembers';
import { getChatMemberCount } from './getChatMemberCount';
import { ChatMemberProfile, EnvOptionsType } from '../types';
import { getGroupMembersPublicKeys } from './getGroupMembersPublicKeys';

export const getAllGroupMembersPublicKeys = async (options: {
chatId: string;
env: EnvOptionsType['env'];
}): Promise<ChatMemberProfile[]> => {
const { chatId, env } = options;
const count = await getChatMemberCount({ chatId, env });
const overallCount = count.approvedCount;
const limit = 5000;
const totalPages = Math.ceil(overallCount / limit);
const pageNumbers = Array.from({ length: totalPages }, (_, i) => i + 1);
const groupMembers: ChatMemberProfile[] = [];

const memberFetchPromises = pageNumbers.map((page) =>
getGroupMembersPublicKeys({ chatId, env, page, limit })
);

const membersResults = await Promise.all(memberFetchPromises);
membersResults.forEach((result) => {
if (result.members.length > 0) {
groupMembers.push(...result.members);
}
});

return groupMembers;
};
2 changes: 1 addition & 1 deletion packages/restapi/src/lib/chat/getChatMemberCount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const getChatMemberCount = async (
}

const API_BASE_URL = getAPIBaseUrls(env);
const requestUrl = `${API_BASE_URL}/v1/chat/${chatId}/members/count`;
const requestUrl = `${API_BASE_URL}/v1/chat/groups/${chatId}/members/count`;

const response = await axios.get(requestUrl);
const { totalMembersCount } = response.data;
Expand Down
2 changes: 1 addition & 1 deletion packages/restapi/src/lib/chat/getGroupMembers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const getGroupMembers = async (
}

const API_BASE_URL = getAPIBaseUrls(env);
const requestUrl = `${API_BASE_URL}/v1/chat/${chatId}/members?pageNumber=${page}&pageSize=${limit}`;
const requestUrl = `${API_BASE_URL}/v1/chat/groups/${chatId}/members?pageNumber=${page}&pageSize=${limit}`;

const response = await axios.get(requestUrl);
return response.data;
Expand Down
38 changes: 38 additions & 0 deletions packages/restapi/src/lib/chat/getGroupMembersPublicKeys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import axios from 'axios';
import { getAPIBaseUrls } from '../helpers';
import Constants, { ENV } from '../constants';
import { ChatMemberCounts, ChatMemberProfile } from '../types';
import { FetchChatGroupInfoType } from './getGroupMembers';

/**
* GET /v1/chat/:chatId/members/public/keys
*/

export const getGroupMembersPublicKeys = async (
options: FetchChatGroupInfoType
): Promise<{
totalMembersCount: ChatMemberCounts;
members: ChatMemberProfile[];
}> => {
const { chatId, page = 1, limit = 20, env = Constants.ENV.PROD } = options;

try {
if (!chatId) {
throw new Error('Chat ID is required.');
}

const API_BASE_URL = getAPIBaseUrls(env);
const requestUrl = `${API_BASE_URL}/v1/chat/groups/${chatId}/members/public/keys?pageNumber=${page}&pageSize=${limit}`;

const response = await axios.get(requestUrl);
return response.data;
} catch (error) {
console.error(
`[Push SDK] - API - Error - API ${getGroupMembersPublicKeys.name} -: `,
error
);
throw new Error(
`[Push SDK] - API - Error - API ${getGroupMembersPublicKeys.name} -: ${error}`
);
}
};
4 changes: 1 addition & 3 deletions packages/restapi/src/lib/chat/helpers/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,7 @@ export const validateGroupMemberUpdateOptions = (
`Invalid role: ${role}. Allowed roles are ${allowedRoles.join(', ')}.`
);
}
if (upsert[role] && upsert[role].length > 100) {
throw new Error(`${role} array cannot have more than 100 addresses.`);
}

// Assuming you have a function `isValidETHAddress` to validate Ethereum addresses
upsert[role].forEach((address) => {
if (!isValidETHAddress(address)) {
Expand Down
1 change: 1 addition & 0 deletions packages/restapi/src/lib/chat/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ export * from './getGroupMemberStatus';
export * from './getGroupMembers';
export * from './getGroupInfo';
export * from './getChatMemberCount';
export * from './getGroupMembersPublicKeys';
1 change: 1 addition & 0 deletions packages/restapi/src/lib/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ export interface ChatMemberCounts {
adminsCount: number;
membersCount: number;
pendingCount: number;
approvedCount: number;
}

export interface ChatMemberProfile {
Expand Down
109 changes: 99 additions & 10 deletions packages/restapi/tests/lib/benchmark/privateGroup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const _env = Constants.ENV.LOCAL;
* THIS TEST GROUP IS FOR BENCHMARKING SEND MESSAGE FOR PRIVATE GROUP
* These tests will be skipped
*/
describe.only('Private Groups', () => {
describe('Private Groups', () => {
let account: string;
let account2: string;
let userAlice: PushAPI;
Expand Down Expand Up @@ -341,16 +341,21 @@ describe.only('Private Groups', () => {
console.log('Duration in ms : ', duration);
});
it('5000 Members', async () => {
await createGroupAndSendMessages(userAlice, 5000);
});
it('10000 Members', async () => {
await createGroupAndSendMessages(userAlice, 10000);
});
it('15000 Members', async () => {
await createGroupAndSendMessages(userAlice, 15000);
});
});

describe('Update Group with Pending members', () => {
it.only('10 Members', async () => {
const chatId =
'33c9295913786a8c446ceca46af8ee29a3a7144ba63071c24e5f05a5407bccdf';
const startTime = new Date();
await userAlice.chat.send(chatId, {
content: 'Sending Message to Private Grp',
type: 'Text',
});
const endTime = new Date();
const duration = endTime.getTime() - startTime.getTime();
console.log('Duration in ms : ', duration);
'd8892a41ccbb7d0c627d1e3976f3a0bd64540d1d535b1a339680f2ce5b0fbcf0';
await updateGroupWithPendingMembers(userAlice, chatId, 500);
});
});

Expand Down Expand Up @@ -465,6 +470,90 @@ const createGroupWithPendingMembers = async (
return createdGroup.chatId;
};


/**
* CREATE GROUP WITH GIVEN MEMBERS COUNT PENDING MEMBERS
* @dev - Added members are pending members
*/
const updateGroupWithPendingMembers = async (
user: PushAPI,
chatId: string,
memberCount: number
): Promise<string> => {
/**
* STEP 1: Generate ENOUGH USERS
*/
console.log('Generating Users');
const users = await generateUsers(memberCount);

/**
* STEP 2: Add Members to Group
* Note - At max 100 members can be added at once
*/
console.log('Adding Members to Group');
let currentMemberCount = 1;
while (currentMemberCount < memberCount) {
const currentUsersIndex = currentMemberCount - 1;
if (currentMemberCount + 100 > memberCount) {
currentMemberCount = memberCount;
} else {
currentMemberCount += 100;
}
const nextUsersIndex = currentMemberCount - 1;

const membersToBeAdded = [];
for (let i = currentUsersIndex; i <= nextUsersIndex; i++) {
membersToBeAdded.push(users[i]);
}
await user.chat.group.add(chatId, {
role: 'MEMBER',
accounts: membersToBeAdded,
});
}
console.log('Added Members to Group : ', currentMemberCount);
return chatId;
};

const generateUsers = async (memberCount: number): Promise<string[]> => {
let users: string[] = []; // Now 'users' is explicitly typed as an array of strings
let generationCount = 0;
const batchSize = 20;

while (generationCount < memberCount) {
const userPromises: Promise<string>[] = []; // An array to hold the promises which will resolve to strings
for (let i = 0; i < batchSize && generationCount < memberCount; i++) {
userPromises.push(
(async () => {
const WALLET = ethers.Wallet.createRandom();
const signer = new ethers.Wallet(WALLET.privateKey);
const account = `eip155:${signer.address}`;
// Assume that PushAPI.initialize resolves successfully and you don't need anything from the resolved value
await PushAPI.initialize(signer, {
env: _env,
streamOptions: { enabled: false },
});
return account; // This resolves to a string
})()
);
generationCount++;
}

// Wait for all promises in the batch to resolve, and then spread their results into the 'users' array
const batchResults = await Promise.all(userPromises);
users = [...users, ...batchResults];

if (generationCount % 100 == 0) {
console.log('Generated Users : ', generationCount);
}
}

console.log(
`User Generation Completed, users generated : ${generationCount}`
);
return users; // 'users' is an array of strings representing accounts
};


/**
* CREATE GROUP WITH GIVEN MEMBERS COUNT NON-PENDING MEMBERS
* @dev - Added members are pending members
Expand Down

0 comments on commit 809b3ee

Please sign in to comment.