Move user and room handling to respective modules

This commit is contained in:
Henrik Hüttemann 2023-06-23 15:26:39 +02:00
parent 3459bbecd3
commit 6b723db5a1
No known key found for this signature in database
GPG Key ID: 9F7BD10E0A8A111E
5 changed files with 119 additions and 52 deletions

View File

@ -2,12 +2,11 @@ import dotenv from 'dotenv'
dotenv.config() dotenv.config()
import lineByLine from 'n-readlines' import lineByLine from 'n-readlines'
import 'reflect-metadata' import 'reflect-metadata'
import { IdMapping } from './entity/IdMapping' import { handle as handleRoom } from './handlers/rooms'
import { RcUser, createUser, userIsExcluded } from './handlers/users' import { handle as handleUser } from './handlers/users'
import log from './helpers/logger' import log from './helpers/logger'
import { getRoomId, getUserId, initStorage, save } from './helpers/storage' import { initStorage } from './helpers/storage'
import { whoami } from './helpers/synapse' import { whoami } from './helpers/synapse'
import { RcRoom, createRoom } from './handlers/rooms'
log.info('rocketchat2matrix starts.') log.info('rocketchat2matrix starts.')
@ -45,47 +44,11 @@ async function loadRcExport(entity: Entities) {
const item = JSON.parse(line.toString()) const item = JSON.parse(line.toString())
switch (entity) { switch (entity) {
case Entities.Users: case Entities.Users:
const rcUser: RcUser = item await handleUser(item)
log.info(`Parsing user: ${rcUser.name}: ${rcUser._id}`)
if (userIsExcluded(rcUser)) {
break
}
const matrixUserId = await getUserId(rcUser._id)
if (matrixUserId) {
log.debug(`Mapping exists: ${rcUser._id} -> ${matrixUserId}`)
} else {
const matrixUser = await createUser(rcUser)
const mapping = new IdMapping()
mapping.rcId = rcUser._id
mapping.matrixId = matrixUser.user_id
mapping.type = entities[entity].mappingType
mapping.accessToken = matrixUser.access_token
await save(mapping)
log.debug('Mapping added:', mapping)
}
break break
case Entities.Rooms: case Entities.Rooms:
const rcRoom: RcRoom = item await handleRoom(item)
log.info(`Parsing room ${rcRoom.name || 'with ID: ' + rcRoom._id}`)
const matrixRoomId = await getRoomId(rcRoom._id)
if (matrixRoomId) {
log.debug(`Mapping exists: ${rcRoom._id} -> ${matrixRoomId}`)
} else {
const matrixRoom = await createRoom(rcRoom)
const roomMapping = new IdMapping()
roomMapping.rcId = rcRoom._id
roomMapping.matrixId = matrixRoom.room_id
roomMapping.type = entities[entity].mappingType
await save(roomMapping)
log.debug('Mapping added:', roomMapping)
}
break break
case Entities.Messages: case Entities.Messages:

View File

@ -6,13 +6,15 @@ import { SessionOptions } from '../helpers/synapse'
import { import {
MatrixRoomPresets, MatrixRoomPresets,
MatrixRoomVisibility, MatrixRoomVisibility,
RcRoom,
RcRoomTypes, RcRoomTypes,
acceptInvitation, acceptInvitation,
createMapping,
getCreator, getCreator,
getFilteredMembers, getFilteredMembers,
inviteMember, inviteMember,
mapRoom, mapRoom,
parseMemberships, createDirectChatMemberships,
registerRoom, registerRoom,
} from './rooms' } from './rooms'
@ -110,10 +112,13 @@ test('getting creator', () => {
expect(getCreator(rcDirectChat)).toBe('aliceid') expect(getCreator(rcDirectChat)).toBe('aliceid')
expect(getCreator(rcPublicRoom)).toBe(roomCreator._id) expect(getCreator(rcPublicRoom)).toBe(roomCreator._id)
expect(getCreator(rcPrivateRoom)).toBe(roomCreator._id) expect(getCreator(rcPrivateRoom)).toBe(roomCreator._id)
expect(getCreator({} as RcRoom)).toBe('')
}) })
test('creating memberships for direct chats', async () => { test('creating memberships for direct chats', async () => {
await expect(parseMemberships(rcDirectChat)).resolves.toBe(undefined) await expect(createDirectChatMemberships(rcDirectChat)).resolves.toBe(
undefined
)
expect(mockedStorage.createMembership).toHaveBeenCalledWith( expect(mockedStorage.createMembership).toHaveBeenCalledWith(
rcDirectChat._id, rcDirectChat._id,
rcDirectChat.uids[0] rcDirectChat.uids[0]
@ -127,7 +132,11 @@ test('creating memberships for direct chats', async () => {
mockedStorage.createMembership.mockClear() mockedStorage.createMembership.mockClear()
await expect( await expect(
parseMemberships({ ...rcDirectChat, _id: 'hoihoi', uids: ['hoi', 'hoi'] }) createDirectChatMemberships({
...rcDirectChat,
_id: 'hoihoi',
uids: ['hoi', 'hoi'],
})
).resolves.toBe(undefined) ).resolves.toBe(undefined)
expect(mockedStorage.createMembership).toHaveBeenCalledWith('hoihoi', 'hoi') expect(mockedStorage.createMembership).toHaveBeenCalledWith('hoihoi', 'hoi')
@ -210,3 +219,15 @@ test('filtering members', async () => {
expect(mockedStorage.getMapping).toBeCalledWith('otherExistingUser', 0) expect(mockedStorage.getMapping).toBeCalledWith('otherExistingUser', 0)
expect(mockedStorage.getMapping).toBeCalledWith('excludedUser', 0) expect(mockedStorage.getMapping).toBeCalledWith('excludedUser', 0)
}) })
test('creating mapping', async () => {
await expect(
createMapping(rcPublicRoom._id, { ...mapRoom(rcPublicRoom), room_id })
).resolves.toBe(undefined)
expect(mockedStorage.save).toHaveBeenCalledWith({
rcId: rcPublicRoom._id,
matrixId: room_id,
type: 1,
accessToken: undefined,
} as IdMapping)
})

View File

@ -4,6 +4,8 @@ import {
createMembership, createMembership,
getMapping, getMapping,
getMemberships, getMemberships,
getRoomId,
save,
} from '../helpers/storage' } from '../helpers/storage'
import { import {
SessionOptions, SessionOptions,
@ -101,7 +103,9 @@ export function getCreator(rcRoom: RcRoom): string {
} }
} }
export async function parseMemberships(rcRoom: RcRoom): Promise<void> { export async function createDirectChatMemberships(
rcRoom: RcRoom
): Promise<void> {
if (rcRoom.t == RcRoomTypes.direct && rcRoom.uids) { if (rcRoom.t == RcRoomTypes.direct && rcRoom.uids) {
await Promise.all( await Promise.all(
[...new Set(rcRoom.uids)] // Deduplicate users [...new Set(rcRoom.uids)] // Deduplicate users
@ -182,19 +186,45 @@ export async function getFilteredMembers(
return memberMappings return memberMappings
} }
export async function createMapping(
rcId: string,
matrixRoom: MatrixRoom
): Promise<void> {
const roomMapping = new IdMapping()
roomMapping.rcId = rcId
roomMapping.matrixId = matrixRoom.room_id
roomMapping.type = 1
await save(roomMapping)
log.debug('Mapping added:', roomMapping)
}
export async function createRoom(rcRoom: RcRoom): Promise<MatrixRoom> { export async function createRoom(rcRoom: RcRoom): Promise<MatrixRoom> {
const room: MatrixRoom = mapRoom(rcRoom) const room: MatrixRoom = mapRoom(rcRoom)
const creatorId = getCreator(rcRoom) const creatorId = getCreator(rcRoom)
await parseMemberships(rcRoom) await createDirectChatMemberships(rcRoom)
const creatorSessionOptions = await getCreatorSessionOptions(creatorId) const creatorSessionOptions = await getCreatorSessionOptions(creatorId)
log.debug('Creating room:', room) log.debug('Creating room:', room)
room.room_id = await registerRoom(room, creatorSessionOptions) room.room_id = await registerRoom(room, creatorSessionOptions)
const rcMemberIds = await getMemberships(rcRoom._id) await handleMemberships(rcRoom._id, room, creatorId, creatorSessionOptions)
return room
}
async function handleMemberships(
rcRoomId: string,
room: MatrixRoom,
creatorId: string,
creatorSessionOptions: object | SessionOptions
) {
const rcMemberIds = await getMemberships(rcRoomId)
const memberMappings = await getFilteredMembers(rcMemberIds, creatorId) const memberMappings = await getFilteredMembers(rcMemberIds, creatorId)
log.info( log.info(
`Inviting members to room ${rcRoom._id}:`, `Inviting members to room ${
room.room_alias_name || room.name || room.room_id
}:`,
memberMappings.map((mapping) => mapping.matrixId) memberMappings.map((mapping) => mapping.matrixId)
) )
log.debug( log.debug(
@ -214,6 +244,16 @@ export async function createRoom(rcRoom: RcRoom): Promise<MatrixRoom> {
await acceptInvitation(memberMapping, room.room_id || '') await acceptInvitation(memberMapping, room.room_id || '')
}) })
) )
}
return room
export async function handle(rcRoom: RcRoom): Promise<void> {
log.info(`Parsing room ${rcRoom.name || 'with ID: ' + rcRoom._id}`)
const matrixRoomId = await getRoomId(rcRoom._id)
if (matrixRoomId) {
log.debug(`Mapping exists: ${rcRoom._id} -> ${matrixRoomId}`)
} else {
const matrixRoom = await createRoom(rcRoom)
await createMapping(rcRoom._id, matrixRoom)
}
} }

View File

@ -6,11 +6,13 @@ import * as storage from '../helpers/storage'
import { import {
MatrixUser, MatrixUser,
RcUser, RcUser,
createMapping,
createUser, createUser,
generateHmac, generateHmac,
mapUser, mapUser,
userIsExcluded, userIsExcluded,
} from '../handlers/users' } from '../handlers/users'
import { IdMapping } from '../entity/IdMapping'
jest.mock('axios') jest.mock('axios')
const mockedAxios = axios as jest.Mocked<typeof axios> const mockedAxios = axios as jest.Mocked<typeof axios>
@ -96,3 +98,13 @@ test('users are excluded', () => {
}) })
).toBeTruthy() ).toBeTruthy()
}) })
test('creating mapping', async () => {
await expect(createMapping(rcUser._id, matrixUser)).resolves.toBe(undefined)
expect(mockedStorage.save).toHaveBeenCalledWith({
rcId: rcUser._id,
matrixId: matrixUser.user_id,
type: 0,
accessToken: matrixUser.access_token,
} as IdMapping)
})

View File

@ -1,7 +1,8 @@
import { createHmac } from 'node:crypto' import { createHmac } from 'node:crypto'
import log from '../helpers/logger' import log from '../helpers/logger'
import { axios } from '../helpers/synapse' import { axios } from '../helpers/synapse'
import { createMembership } from '../helpers/storage' import { createMembership, getUserId, save } from '../helpers/storage'
import { IdMapping } from '../entity/IdMapping'
export type RcUser = { export type RcUser = {
_id: string _id: string
@ -91,6 +92,20 @@ export function userIsExcluded(rcUser: RcUser): boolean {
return false return false
} }
export async function createMapping(
rcId: string,
matrixUser: MatrixUser
): Promise<void> {
const mapping = new IdMapping()
mapping.rcId = rcId
mapping.matrixId = matrixUser.user_id
mapping.type = 0
mapping.accessToken = matrixUser.access_token
await save(mapping)
log.debug('Mapping added:', mapping)
}
export async function createUser(rcUser: RcUser): Promise<MatrixUser> { export async function createUser(rcUser: RcUser): Promise<MatrixUser> {
const user = mapUser(rcUser) const user = mapUser(rcUser)
const nonce = await getUserRegistrationNonce() const nonce = await getUserRegistrationNonce()
@ -104,3 +119,19 @@ export async function createUser(rcUser: RcUser): Promise<MatrixUser> {
return user return user
} }
export async function handle(rcUser: RcUser): Promise<void> {
log.info(`Parsing user: ${rcUser.name}: ${rcUser._id}`)
if (userIsExcluded(rcUser)) {
return undefined
}
const matrixId = await getUserId(rcUser._id)
if (matrixId) {
log.debug(`Mapping exists: ${rcUser._id} -> ${matrixId}`)
} else {
const matrixUser = await createUser(rcUser)
await createMapping(rcUser._id, matrixUser)
}
}