From a702e7b0ca814fdf3d595fd374839360c68a385f Mon Sep 17 00:00:00 2001 From: Mohammed S Date: Sun, 24 Sep 2023 00:56:42 +0530 Subject: [PATCH] fix: PUSH Stream Changes --- packages/examples/sdk-backend-node/main.ts | 6 +- .../src/lib/chat/getGroupMemberStatus.ts | 2 - packages/restapi/src/lib/config.ts | 2 +- packages/restapi/src/lib/pushapi/PushAPI.ts | 28 +++++-- .../src/lib/pushstream/DataModifier.ts | 56 ++++++++++--- .../restapi/src/lib/pushstream/PushStream.ts | 20 +++-- .../src/lib/pushstream/pushStreamTypes.ts | 43 ++++++++-- .../tests/lib/pushstream/initialize.test.ts | 79 +++++++++++++------ 8 files changed, 179 insertions(+), 57 deletions(-) diff --git a/packages/examples/sdk-backend-node/main.ts b/packages/examples/sdk-backend-node/main.ts index 1eec0a63a..dbb09f210 100644 --- a/packages/examples/sdk-backend-node/main.ts +++ b/packages/examples/sdk-backend-node/main.ts @@ -6,7 +6,6 @@ import { runPushAPICases } from './pushAPI'; import { config } from './config'; import { ENV } from './types'; -import { runPushStreamCases } from './pushStream'; // CONFIGS const { env } = config; @@ -16,13 +15,12 @@ const start = async (): Promise => { console.log(`${returnHeadingLog()}`); console.log(`${returnENVLog()}`); - await runPushStreamCases(); - /*await runPushAPICases(); + await runPushAPICases(); await runNotificaitonsUseCases(); await runChatUseCases(); await runNFTChatUseCases(); await runVideoUseCases(); - await runSpacesUseCases();*/ + await runSpacesUseCases(); }; start(); diff --git a/packages/restapi/src/lib/chat/getGroupMemberStatus.ts b/packages/restapi/src/lib/chat/getGroupMemberStatus.ts index ddfd223b4..8c9f0282c 100644 --- a/packages/restapi/src/lib/chat/getGroupMemberStatus.ts +++ b/packages/restapi/src/lib/chat/getGroupMemberStatus.ts @@ -33,8 +33,6 @@ export const getGroupMemberStatus = async ( const API_BASE_URL = getAPIBaseUrls(env); const requestUrl = `${API_BASE_URL}/v1/chat/groups/${chatId}/members/${user}/status`; - console.log(requestUrl) - return axios .get(requestUrl) .then((response) => { diff --git a/packages/restapi/src/lib/config.ts b/packages/restapi/src/lib/config.ts index 28aac1d90..c4ed49fb2 100644 --- a/packages/restapi/src/lib/config.ts +++ b/packages/restapi/src/lib/config.ts @@ -6,7 +6,7 @@ const { ENV } = Constants; export const API_BASE_URL = { [ENV.PROD]: 'https://backend.epns.io/apis', [ENV.STAGING]: 'https://backend-staging.epns.io/apis', - [ENV.DEV]: 'http://localhost:4000/apis', + [ENV.DEV]: 'https://backend-dev.epns.io/apis', /** * **This is for local development only** diff --git a/packages/restapi/src/lib/pushapi/PushAPI.ts b/packages/restapi/src/lib/pushapi/PushAPI.ts index d501e7712..9603b0977 100644 --- a/packages/restapi/src/lib/pushapi/PushAPI.ts +++ b/packages/restapi/src/lib/pushapi/PushAPI.ts @@ -508,6 +508,7 @@ export class PushAPI { const status = await PUSH_CHAT.getGroupMemberStatus({ chatId: target, did: this.account, + env: this.env, }); if (status.isPending) { @@ -536,14 +537,31 @@ export class PushAPI { }, leave: async (target: string): Promise => { - return await PUSH_CHAT.removeMembers({ + const status = await PUSH_CHAT.getGroupMemberStatus({ chatId: target, - members: [this.account], + did: this.account, env: this.env, - account: this.account, - signer: this.signer, - pgpPrivateKey: this.decryptedPgpPvtKey, }); + + if (status.isAdmin) { + return await PUSH_CHAT.removeAdmins({ + chatId: target, + admins: [this.account], + env: this.env, + account: this.account, + signer: this.signer, + pgpPrivateKey: this.decryptedPgpPvtKey, + }); + } else { + return await PUSH_CHAT.removeMembers({ + chatId: target, + members: [this.account], + env: this.env, + account: this.account, + signer: this.signer, + pgpPrivateKey: this.decryptedPgpPvtKey, + }); + } }, reject: async (target: string): Promise => { diff --git a/packages/restapi/src/lib/pushstream/DataModifier.ts b/packages/restapi/src/lib/pushstream/DataModifier.ts index f1c820088..50b42f69f 100644 --- a/packages/restapi/src/lib/pushstream/DataModifier.ts +++ b/packages/restapi/src/lib/pushstream/DataModifier.ts @@ -8,6 +8,8 @@ import { MessageEventType, Member, GroupEventType, + LeaveGroupEvent, + JoinGroupEvent, } from './pushStreamTypes'; export class DataModifier { @@ -16,6 +18,13 @@ export class DataModifier { return this.mapToCreateGroupEvent(data, includeRaw); } else if (data.eventType === 'update') { return this.mapToUpdateGroupEvent(data, includeRaw); + } else if ( + data.eventType === GroupEventType.JoinGroup || + data.eventType === GroupEventType.LeaveGroup || + data.eventType === MessageEventType.Request || + data.eventType === GroupEventType.Remove + ) { + return this.mapToGroupMemberEvent(data, includeRaw, data.eventType); } else { console.warn('Unknown eventType:', data.eventType); return data; @@ -113,7 +122,7 @@ export class DataModifier { includeRaw: boolean ): CreateGroupEvent { return this.mapToGroupEvent( - GroupEventType.createGroup, + GroupEventType.CreateGroup, incomingData, includeRaw ) as CreateGroupEvent; @@ -124,7 +133,7 @@ export class DataModifier { includeRaw: boolean ): UpdateGroupEvent { return this.mapToGroupEvent( - GroupEventType.updateGroup, + GroupEventType.UpdateGroup, incomingData, includeRaw ) as UpdateGroupEvent; @@ -173,20 +182,49 @@ export class DataModifier { public static handleChatEvent(data: any, includeRaw = false): any { const eventTypeMap: { [key: string]: MessageEventType } = { - Chat: 'message', - Request: 'request', - Approve: 'accept', - Reject: 'reject', + Chat: MessageEventType.Message, + Request: MessageEventType.Request, + Approve: MessageEventType.Accept, + Reject: MessageEventType.Reject, }; const eventType: MessageEventType | undefined = - eventTypeMap[data.messageCategory]; + eventTypeMap[data.eventType || data.messageCategory]; if (eventType) { - return this.mapToMessageEvent(data, includeRaw, eventType); + return this.mapToMessageEvent( + data, + includeRaw, + eventType as MessageEventType + ); } else { - console.warn('Unknown messageCategory:', data.messageCategory); + console.warn( + 'Unknown eventType:', + data.eventType || data.messageCategory + ); return data; } } + + private static mapToGroupMemberEvent( + data: any, + includeRaw: boolean, + eventType: GroupEventType + ): JoinGroupEvent | LeaveGroupEvent { + const baseEventData = { + origin: data.messageOrigin, + timestamp: data.timestamp, + chatId: data.chatId, + from: data.from, + to: data.to, + event: eventType as GroupEventType.JoinGroup | GroupEventType.LeaveGroup, + }; + + return includeRaw + ? { + ...baseEventData, + raw: { verificationProof: data.verificationProof }, + } + : baseEventData; + } } diff --git a/packages/restapi/src/lib/pushstream/PushStream.ts b/packages/restapi/src/lib/pushstream/PushStream.ts index 5e0ed9ea1..e572f1982 100644 --- a/packages/restapi/src/lib/pushstream/PushStream.ts +++ b/packages/restapi/src/lib/pushstream/PushStream.ts @@ -1,7 +1,7 @@ import { EventEmitter } from 'events'; import { createSocketConnection, EVENTS } from '@pushprotocol/socket'; import { ENV } from '../constants'; -import { PushStreamInitializeProps, STREAM } from './pushStreamTypes'; +import { GroupEventType, MessageEventType, PushStreamInitializeProps, STREAM } from './pushStreamTypes'; import { DataModifier } from './DataModifier'; export class PushStream extends EventEmitter { @@ -57,10 +57,20 @@ export class PushStream extends EventEmitter { } public async init(): Promise { - this.pushChatSocket.on(EVENTS.CHAT_GROUPS, (data: any) => { - const modifiedData = DataModifier.handleChatGroupEvent(data, this.raw); - this.emit(STREAM.CHAT_OPS, modifiedData); - }); + this.pushChatSocket.on(EVENTS.CHAT_GROUPS, (data: any) => { + const modifiedData = DataModifier.handleChatGroupEvent(data, this.raw); + if ( + data.eventType === GroupEventType.JoinGroup || + data.eventType === GroupEventType.LeaveGroup || + data.eventType === MessageEventType.Request || + data.eventType === GroupEventType.Remove + ) { + this.emit(STREAM.CHAT, modifiedData); + } else { + this.emit(STREAM.CHAT_OPS, modifiedData); + } + }); + this.pushChatSocket.on(EVENTS.CHAT_RECEIVED_MESSAGE, (data: any) => { const modifiedData = DataModifier.handleChatEvent(data, this.raw); diff --git a/packages/restapi/src/lib/pushstream/pushStreamTypes.ts b/packages/restapi/src/lib/pushstream/pushStreamTypes.ts index 3c475dde2..f2bf10c0b 100644 --- a/packages/restapi/src/lib/pushstream/pushStreamTypes.ts +++ b/packages/restapi/src/lib/pushstream/pushStreamTypes.ts @@ -26,11 +26,24 @@ export enum STREAM { DISCONNECT = 'STREAM.DISCONNECT', } -export type MessageOrigin = 'other' | 'self'; -export type MessageEventType = 'message' | 'request' | 'accept' | 'reject'; +export enum MessageOrigin { + Other = 'other', + Self = 'self', +} + +export enum MessageEventType { + Message = 'message', + Request = 'request', + Accept = 'accept', + Reject = 'reject', +} + export enum GroupEventType { - createGroup = 'createGroup', - updateGroup = 'updateGroup', + CreateGroup = 'createGroup', + UpdateGroup = 'updateGroup', + JoinGroup = 'joinGroup', + LeaveGroup = 'leaveGroup', + Remove = 'remove', } export interface Profile { @@ -75,11 +88,29 @@ export interface GroupEventBase { } export interface CreateGroupEvent extends GroupEventBase { - event: GroupEventType.createGroup; + event: GroupEventType.CreateGroup; } export interface UpdateGroupEvent extends GroupEventBase { - event: GroupEventType.updateGroup; + event: GroupEventType.UpdateGroup; +} + +export interface GroupMemberEventBase { + event: GroupEventType; + origin: MessageOrigin; + timestamp: string; + chatId: string; + from: string; + to: string[]; + raw?: GroupEventRawData; +} + +export interface JoinGroupEvent extends GroupMemberEventBase { + event: GroupEventType.JoinGroup; +} + +export interface LeaveGroupEvent extends GroupMemberEventBase { + event: GroupEventType.LeaveGroup; } export interface MessageEvent { diff --git a/packages/restapi/tests/lib/pushstream/initialize.test.ts b/packages/restapi/tests/lib/pushstream/initialize.test.ts index 1d8ebd875..f293ad58a 100644 --- a/packages/restapi/tests/lib/pushstream/initialize.test.ts +++ b/packages/restapi/tests/lib/pushstream/initialize.test.ts @@ -7,6 +7,7 @@ import { ethers } from 'ethers'; import { PushAPI } from '../../../src/lib/pushapi/PushAPI'; import { ENV } from '../../../src/lib/constants'; import { STREAM } from '../../../src/lib/pushstream/pushStreamTypes'; +import * as util from 'util'; describe.only('PushStream.initialize functionality', () => { it('Should initialize new stream and listen to events', async () => { @@ -76,7 +77,7 @@ describe.only('PushStream.initialize functionality', () => { const CREATE_GROUP_REQUEST = { description: 'test', image: 'test', - members: [signer2.address], + members: [], admins: [], private: false, rules: {}, @@ -91,20 +92,23 @@ describe.only('PushStream.initialize functionality', () => { ) => { return new Promise((resolve, reject) => { let eventCount = 0; + if (expectedEventCount == 0) { + resolve('Done'); + } const receivedEvents: any[] = []; stream.on(eventType, (data: any) => { try { receivedEvents.push(data); eventCount++; - /*console.log( + console.log( `Event ${eventCount} for ${expectedEvent}:`, util.inspect(data, { showHidden: false, depth: null, colors: true, }) - );*/ + ); expect(data).to.not.be.null; if (eventCount === expectedEventCount) { @@ -118,8 +122,11 @@ describe.only('PushStream.initialize functionality', () => { }); }; - const onDataReceived = createEventPromise('CHAT_OPS', STREAM.CHAT_OPS, 1); - const onMessageReceived = createEventPromise('CHAT', STREAM.CHAT, 1); + // leave admin bug + // group creator check remove add + + const onDataReceived = createEventPromise('CHAT_OPS', STREAM.CHAT_OPS, 2); + const onMessageReceived = createEventPromise('CHAT', STREAM.CHAT, 3); // Create and update group const createdGroup = await user.chat.group.create( @@ -127,11 +134,30 @@ describe.only('PushStream.initialize functionality', () => { CREATE_GROUP_REQUEST ); + console.log(createdGroup.chatId); + console.log(signer2.address); + + //const w2wRejectRequest = await user2.chat.group.join(createdGroup.chatId); + + const updatedGroup = await user.chat.group.add(createdGroup.chatId, { + role: 'ADMIN', + accounts: [signer2.address, signer3.address, signer4.address], + }); + const w2wRejectRequest = await user2.chat.group.join(createdGroup.chatId); - console.log(w2wRejectRequest); + const updatedGroup2 = await user2.chat.group.leave(createdGroup.chatId); + + /*const w2wMessageResponse = await user2.chat.send(signer.address, { + content: MESSAGE, + }); + const w2wAcceptsRequest = await user.chat.accept(signer2.address); + + const w2wMessageResponse2 = await user2.chat.send(signer.address, { + content: MESSAGE, + }); - /*const updatedGroup = await user.chat.group.update(createdGroup.chatId, { + const updatedGroup = await user.chat.group.update(createdGroup.chatId, { description: 'Updated Description', }); @@ -140,10 +166,7 @@ describe.only('PushStream.initialize functionality', () => { type: MessageType.TEXT, }); - const w2wMessageResponse = await user2.chat.send(signer.address, { - content: MESSAGE, - }); - const w2wAcceptsRequest = await user.chat.accept(signer2.address); + const w2wMessageResponse2 = await user2.chat.send(signer.address, { content: MESSAGE, @@ -154,27 +177,33 @@ describe.only('PushStream.initialize functionality', () => { }); const w2wRejectRequest = await user.chat.reject(signer3.address);*/ - // Timeout promise + let timeoutTriggered = false; + const timeout = new Promise((_, reject) => { - setTimeout(() => reject(new Error('Timeout after 5 seconds')), 5); + setTimeout(() => { + timeoutTriggered = true; + reject(new Error('Timeout after 5 seconds')); + }, 5000); }); - // Wait for either one of the events to be emitted or for the timeout + // Wrap the Promise.allSettled inside a Promise.race with the timeout try { - const results = await Promise.allSettled([ - onDataReceived, - onMessageReceived, + const result = await Promise.race([ + Promise.allSettled([onDataReceived, onMessageReceived]), timeout, ]); - results.forEach((result) => { - if (result.status === 'rejected') { - console.error(result.reason); - } else { - // Handle result.value if necessary - console.log(result.value); - } - }); + if (timeoutTriggered) { + console.error('Timeout reached before events were emitted.'); + } else { + (result as PromiseSettledResult[]).forEach((outcome) => { + if (outcome.status === 'fulfilled') { + //console.log(outcome.value); + } else if (outcome.status === 'rejected') { + console.error(outcome.reason); + } + }); + } } catch (error) { console.error(error); }