Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CF SDK - Implicit reattach #1110

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
127 changes: 117 additions & 10 deletions internal/e2e-js/tests/callfabric/reattach.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@ import { test, expect } from '../../fixtures'
import {
SERVER_URL,
createCFClient,
expectMCUVisible
expectCallJoined,
expectMCUVisible,
getRemoteMediaIP,
} from '../../utils'

test.describe('Reattach Tests', () => {
test('WebRTC to Room', async ({
createCustomPage,
resource,
}) => {

test.describe('CallFabric Reattach', () => {
test('WebRTC to Room', async ({ createCustomPage, resource }) => {
const page = await createCustomPage({ name: '[page]' })
await page.goto(SERVER_URL)

Expand Down Expand Up @@ -48,7 +46,7 @@ test.describe('Reattach Tests', () => {

await expectMCUVisible(page)

await page.reload({ waitUntil: 'domcontentloaded'})
await page.reload({ waitUntil: 'domcontentloaded' })
await createCFClient(page)

// Reattach to an address to join the same call session
Expand All @@ -74,10 +72,119 @@ test.describe('Reattach Tests', () => {
)

expect(roomSession.call_id).toEqual(currentCallId)
// TODO the server is not sending a layout state on reattach
// TODO the server is not sending a layout state on reattach
// await expectMCUVisible(page)
})

test('should reattach on call dial implicitly', async ({
createCustomPage,
resource,
}) => {
const page = await createCustomPage({ name: '[page]' })
await page.goto(SERVER_URL)

const roomName = `e2e-video-room-reattach_${uuid()}`
await resource.createVideoRoomResource(roomName)

await createCFClient(page)

// Dial an address and join a video room
const roomSession = await expectCallJoined(page, {
to: `/public/${roomName}`,
})

expect(roomSession.room_session).toBeDefined()
const remoteIP = await getRemoteMediaIP(page)

await expectMCUVisible(page)

// --------------- Reattaching ---------------
await page.reload()

await createCFClient(page)

console.time('reattach-time')
// Dial the same address and join a video room
const roomSessionReattached = await expectCallJoined(page, {
to: `/public/${roomName}`,
})
console.timeEnd('reattach-time')

expect(roomSessionReattached.room_session).toBeDefined()
const remoteIPReattached = await getRemoteMediaIP(page)

await expectMCUVisible(page)

expect(roomSession.call_id).toBe(roomSessionReattached.call_id)
expect(roomSession.member_id).toBe(roomSessionReattached.member_id)

// Ask @Giacomo; do we need to compare the remote IP?
expect(remoteIP).toBe(remoteIPReattached)
giavac marked this conversation as resolved.
Show resolved Hide resolved
})

test('should fail reattach with bad auth', async ({
createCustomPage,
resource,
}) => {
const page = await createCustomPage({ name: '[page]' })
await page.goto(SERVER_URL)

const roomName = `e2e-video-room-reattach-bad-auth_${uuid()}`
await resource.createVideoRoomResource(roomName)

await createCFClient(page)

// Dial an address and join a video room
const roomSession = await expectCallJoined(page, {
to: `/public/${roomName}`,
})
expect(roomSession.room_session).toBeDefined()

await expectMCUVisible(page)

// --------------- Reattaching ---------------
await page.reload()

await createCFClient(page)

console.time('reattach-time')
// Dial the same address with a bogus authorization_state
const roomSessionReattached = await page.evaluate(
async ({ roomName }) => {
// Inject wrong values for authorization state
const key = 'as-SAT'
const state = btoa('just wrong')
window.sessionStorage.setItem(key, state)
console.log(
`Injected authorization state for ${key} with value ${state}`
)

// @ts-expect-error
const client = window._client
const call = await client.dial({
to: `/public/${roomName}`,
rootElement: document.getElementById('rootElement'),
})
// @ts-expect-error
window._roomObj = call

// Now try to reattach, which should not succeed
return call.start().catch((error: any) => error)
},
{ roomName }
)
console.timeEnd('reattach-time')

const { code, message } = roomSessionReattached

expect([-32002, '27']).toContain(code)
expect([
'CALL ERROR',
'DESTINATION_OUT_OF_ORDER',
'Cannot reattach this call with this member ID',
]).toContain(message)
})

// TODO uncomment after fixed in the backend
// test('WebRTC to SWML to Room', async ({
// createCustomPage,
Expand Down Expand Up @@ -177,7 +284,7 @@ test.describe('Reattach Tests', () => {
// )

// expect(roomSession.call_id).toEqual(currentCallId)
// // TODO the server is not sending a layout state on reattach
// // TODO the server is not sending a layout state on reattach
// // await expectMCUVisible(page)
// })
})
37 changes: 30 additions & 7 deletions internal/e2e-js/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Video } from '@signalwire/js'
import type { DialParams, Video } from '@signalwire/js'
import type { MediaEvent } from '@signalwire/webrtc'
import { createServer } from 'vite'
import path from 'path'
Expand Down Expand Up @@ -762,8 +762,8 @@ export const createCallWithCompatibilityApi = async (

if (Number.isInteger(Number(response.status)) && response.status !== null) {
if (response.status !== 201) {
const responseBody = await response.json();
const formattedBody = JSON.stringify(responseBody, null, 2);
const responseBody = await response.json()
const formattedBody = JSON.stringify(responseBody, null, 2)

console.log(
'ERROR - response from REST API: ',
Expand Down Expand Up @@ -999,19 +999,21 @@ export const expectInjectRelayHost = async (page: Page, host: string) => {
)
}

export const expectInjectIceTransportPolicy = async (page: Page, iceTransportPolicy: string) => {
export const expectInjectIceTransportPolicy = async (
page: Page,
iceTransportPolicy: string
) => {
await page.evaluate(
async (params) => {
// @ts-expect-error
window.__iceTransportPolicy = params.iceTransportPolicy
},
{
iceTransportPolicy
iceTransportPolicy,
}
)
}


export const expectRelayConnected = async (
page: Page,
envRelayProject: string,
Expand Down Expand Up @@ -1086,12 +1088,33 @@ export const expectCFFinalEvents = (
roomObj.on('destroy', () => resolve(true))
})

return callLeft;
return callLeft
})

return Promise.all([finalEvents, ...extraEvents])
}

export const expectCallJoined = (page: Page, options: DialParams) => {
// @ts-expect-error
return page.evaluate((options) => {
return new Promise<any>(async (resolve, reject) => {
// @ts-expect-error
const client = window._client

const call = await client.dial({
rootElement: document.getElementById('rootElement'),
...options,
})

call.on('call.joined', resolve)

// @ts-expect-error
window._roomObj = call

await call.start().catch(reject)
})
}, options)
}

export interface Resource {
id: string
Expand Down
66 changes: 27 additions & 39 deletions packages/js/src/fabric/CallFabricRoomSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import {
VideoMemberEntity,
Rooms,
VideoLayoutChangedEventParams,
VideoRoomSubscribedEventParams,
RoomSessionMember,
getLogger,
} from '@signalwire/core'
import {
BaseRoomSession,
Expand All @@ -22,8 +20,6 @@ import {
MemberCommandWithValueParams,
} from '../video'
import { BaseConnection } from '@signalwire/webrtc'
import { getStorage } from '../utils/storage'
import { PREVIOUS_CALLID_STORAGE_KEY } from './utils/constants'

interface ExecuteActionParams {
method: JSONRPCMethod
Expand Down Expand Up @@ -88,6 +84,10 @@ export class CallFabricRoomSessionConnection extends RoomSessionConnection {
return this._lastLayoutEvent
}

start() {
return this.join()
}

private executeAction<
InputType,
OutputType = InputType,
Expand Down Expand Up @@ -136,41 +136,29 @@ export class CallFabricRoomSessionConnection extends RoomSessionConnection {
})
}

public async start() {
return new Promise<void>(async (resolve, reject) => {
try {
this.once(
'room.subscribed',
({ call_id }: VideoRoomSubscribedEventParams) => {
getStorage()?.setItem(PREVIOUS_CALLID_STORAGE_KEY, call_id)
resolve()
}
)

this.once('destroy', () => {
getStorage()?.removeItem(PREVIOUS_CALLID_STORAGE_KEY)
})

await this.join()
} catch (error) {
this.logger.error('WSClient call start', error)
reject(error)
}
})
}

override async join() {
if (this.options.attach) {
this.options.prevCallId =
getStorage()?.getItem(PREVIOUS_CALLID_STORAGE_KEY) ?? undefined
}
getLogger().debug(
`Tying to reattach to previuos call? ${!!this.options
.prevCallId} - prevCallId: ${this.options.prevCallId}`
)

return super.join()
}
// public async start() {
// return new Promise<void>(async (resolve, reject) => {
// try {
// await this.join()
// } catch (error) {
// this.logger.error('WSClient call start', error)
// reject(error)
// }
// })
// }

// override async join() {
// if (this.options.attach) {
// this.options.prevCallId =
// getStorage()?.getItem(PREVIOUS_CALLID_STORAGE_KEY) ?? undefined
// }
// getLogger().debug(
// `Tying to reattach to previuos call? ${!!this.options
// .prevCallId} - prevCallId: ${this.options.prevCallId}`
// )

// return super.join()
// }

/** @internal */
public override async resume() {
Expand Down
Loading
Loading