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

feat: send device info changes to connected peers #508

Merged
merged 3 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions src/mapeo-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,16 @@ export class MapeoManager extends TypedEmitter {
*/
#replicate(noiseStream) {
const replicationStream = this.#localPeers.connect(noiseStream)
Promise.all([this.getDeviceInfo(), openedNoiseSecretStream(noiseStream)])
.then(([{ name }, openedNoiseStream]) => {
if (openedNoiseStream.destroyed || !name) return

openedNoiseSecretStream(noiseStream)
.then((openedNoiseStream) => {
if (openedNoiseStream.destroyed) return

const { name } = this.getDeviceInfo()
if (!name) return

const peerId = keyToId(openedNoiseStream.remotePublicKey)

return this.#localPeers.sendDeviceInfo(peerId, {
name,
deviceType: this.#deviceType,
Expand All @@ -274,6 +280,7 @@ export class MapeoManager extends TypedEmitter {
e
)
})

return replicationStream
}

Expand Down Expand Up @@ -385,7 +392,7 @@ export class MapeoManager extends TypedEmitter {
await project.$setProjectSettings(settings)

// 6. Write device info into project
const deviceInfo = await this.getDeviceInfo()
const deviceInfo = this.getDeviceInfo()
if (deviceInfo.name) {
await project[kSetOwnDeviceInfo]({ name: deviceInfo.name })
}
Expand Down Expand Up @@ -580,7 +587,7 @@ export class MapeoManager extends TypedEmitter {
const project = await this.getProject(projectPublicId)

try {
const deviceInfo = await this.getDeviceInfo()
const deviceInfo = this.getDeviceInfo()
if (deviceInfo.name) {
await project[kSetOwnDeviceInfo]({ name: deviceInfo.name })
}
Expand Down Expand Up @@ -689,20 +696,28 @@ export class MapeoManager extends TypedEmitter {
.run()

const listedProjects = await this.listProjects()

await Promise.all(
listedProjects.map(async ({ projectId }) => {
const project = await this.getProject(projectId)
await project[kSetOwnDeviceInfo](deviceInfo)
})
)

await Promise.all(
this.#localPeers.peers
.filter(({ status }) => status === 'connected')
.map((peer) =>
this.#localPeers.sendDeviceInfo(peer.deviceId, deviceInfo)
)
)

this.#l.log('set device info %o', deviceInfo)
}

/**
* @returns {Promise<{ deviceId: string } & Partial<import('./schema/client.js').DeviceInfoParam>>}
* @returns {{ deviceId: string } & Partial<import('./schema/client.js').DeviceInfoParam>}
*/
async getDeviceInfo() {
getDeviceInfo() {
const row = this.#db
.select()
.from(localDeviceInfoTable)
Expand Down
34 changes: 32 additions & 2 deletions test-e2e/device-info.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// @ts-check
import { test } from 'brittle'
import { randomBytes } from 'crypto'
import { once } from 'node:events'
import { KeyManager } from '@mapeo/crypto'
import RAM from 'random-access-memory'
import Fastify from 'fastify'
import { connectPeers, createManagers, waitForPeers } from './utils.js'

import { MapeoManager } from '../src/mapeo-manager.js'

Expand All @@ -26,12 +28,12 @@ test('write and read deviceInfo', async (t) => {

const info1 = { name: 'my device' }
await manager.setDeviceInfo(info1)
const readInfo1 = await manager.getDeviceInfo()
const readInfo1 = manager.getDeviceInfo()
const expected1 = { ...info1, deviceId: manager.deviceId }
t.alike(readInfo1, expected1)
const info2 = { name: 'new name' }
await manager.setDeviceInfo(info2)
const readInfo2 = await manager.getDeviceInfo()
const readInfo2 = manager.getDeviceInfo()
const expected2 = { ...info2, deviceId: manager.deviceId }
t.alike(readInfo2, expected2)
})
Expand Down Expand Up @@ -138,3 +140,31 @@ test('device info written to projects', (t) => {

// TODO: Test closing project, changing name, and getting project to see if device info for project is updated
})

test('device info sent to peers', async (t) => {
const managers = await createManagers(3, t)
const disconnectPeers = connectPeers(managers, { discovery: true })
t.teardown(disconnectPeers)
await waitForPeers(managers, { waitForDeviceInfo: true })

const managerThatChangesName = managers[0]
const otherManagers = managers.slice(1)

/** @param {{ deviceId: string }} peer */
const isChangedPeer = (peer) =>
peer.deviceId === managerThatChangesName.deviceId

const otherManagersReceivedNameChangePromise = Promise.all(
otherManagers.map(async (manager) => {
const [peersFromEvent] = await once(manager, 'local-peers')
t.is(peersFromEvent.find(isChangedPeer)?.name, 'new name')

const updatedLocalPeers = await manager.listLocalPeers()
t.is(updatedLocalPeers.find(isChangedPeer)?.name, 'new name')
})
)

await managerThatChangesName.setDeviceInfo({ name: 'new name' })

await otherManagersReceivedNameChangePromise
})
12 changes: 6 additions & 6 deletions test-e2e/local-peers.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ test('Local peers discovery each other and share device info', async (t) => {
t.teardown(() => disconnectPeers(managers))
await waitForPeers(managers, { waitForDeviceInfo: true })
const deviceInfos = [
...(await Promise.all(mobileManagers.map((m) => m.getDeviceInfo()))).map(
(deviceInfo) => ({ ...deviceInfo, deviceType: 'mobile' })
),
...(await Promise.all(desktopManagers.map((m) => m.getDeviceInfo()))).map(
(deviceInfo) => ({ ...deviceInfo, deviceType: 'desktop' })
),
...mobileManagers
.map((m) => m.getDeviceInfo())
.map((deviceInfo) => ({ ...deviceInfo, deviceType: 'mobile' })),
...desktopManagers
.map((m) => m.getDeviceInfo())
.map((deviceInfo) => ({ ...deviceInfo, deviceType: 'desktop' })),
]
const mPeers = await Promise.all(managers.map((m) => m.listLocalPeers()))
for (const [i, peers] of mPeers.entries()) {
Expand Down
6 changes: 3 additions & 3 deletions test-e2e/members.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { kDataTypes } from '../src/mapeo-project.js'
test('getting yourself after creating project', async (t) => {
const [manager] = await createManagers(1, t)

const deviceInfo = await manager.getDeviceInfo()
const deviceInfo = manager.getDeviceInfo()
const project = await manager.getProject(await manager.createProject())

const me = await project.$member.getById(project.deviceId)
Expand Down Expand Up @@ -54,7 +54,7 @@ test('getting yourself after creating project', async (t) => {
test('getting yourself after adding project (but not yet synced)', async (t) => {
const [manager] = await createManagers(1, t)

const deviceInfo = await manager.getDeviceInfo()
const deviceInfo = manager.getDeviceInfo()
const project = await manager.getProject(
await manager.addProject(
{
Expand Down Expand Up @@ -128,7 +128,7 @@ test('getting invited member after invite accepted', async (t) => {
connectPeers(managers)
await waitForPeers(managers)

const { name: inviteeName } = await invitee.getDeviceInfo()
const { name: inviteeName } = invitee.getDeviceInfo()
const projectId = await invitor.createProject({ name: 'Mapeo' })
const project = await invitor.getProject(projectId)

Expand Down
Loading