import Axios , { AxiosRequestConfig } from 'axios' ; import { ArrayUtil } from '@wireapp/commons' ; import { BackendError , HttpClient , RequestCancelable , SyntheticErrorLabel } from '../http/' ; import type { Activate , ActivationResponse , CheckHandles , CompletePasswordReset , HandleInfo , LimitedQualifiedUserIdList , NewPasswordReset , QualifiedHandle , QualifiedId , SearchResult , SendActivationCode , User , UserPreKeyBundleMap , VerifyDelete , } from '../user/' ; import { RequestCancellationError } from './UserError' ; import type { ClientPreKey , PreKeyBundle } from '../auth/' ; import type { PublicClient , QualifiedPublicClients } from '../client/' ; import type { RichInfo } from './RichInfo' ; import type { UserClients , QualifiedUserClients } from '../conversation/' ; import type { QualifiedUserPreKeyBundleMap } from './UserPreKeyBundleMap' ; export class UserAPI { public static readonly DEFAULT_USERS_CHUNK_SIZE = 50 ; public static readonly DEFAULT_USERS_PREKEY_BUNDLE_CHUNK_SIZE = 128 ; public static readonly URL = { ACTIVATE : '/activate' , BY_HANDLE : 'by-handle' , CALLS : '/calls' , CLIENTS : 'clients' , CONFIG : 'config' , CONTACTS : 'contacts' , DELETE : '/delete' , EMAIL : 'email' , HANDLES : 'handles' , LIST_CLIENTS : 'list-clients' , LIST_PREKEYS : 'list-prekeys' , LIST_USERS : '/list-users' , PASSWORD_RESET : '/password-reset' , PRE_KEYS : 'prekeys' , PROPERTIES : '/properties' , RICH_INFO : 'rich-info' , SEARCH : '/search' , SEND : 'send' , USERS : '/users' , V2 : 'v2' , } ; constructor ( private readonly client ) { } see https public async deleteProperties ( ) { const config = { method : 'delete' , url : UserAPI . URL . PROPERTIES , } ; await this . client . sendJSON ( config ) ; } param propertyKey see https public async deleteProperty ( propertyKey ) { const config = { method : 'delete' , url : `${ UserAPI . URL . PROPERTIES }/${ propertyKey }` , } ; await this . client . sendJSON ( config ) ; } param activationCode param activationKey see https public async getActivation ( activationCode , activationKey ) { const config = { method : 'get' , params : { code : activationCode , key : activationKey , } , url : UserAPI . URL . ACTIVATE , } ; const response = await this . client . sendJSON < ActivationResponse > ( config ) ; return response . data ; } see https public async getCallsConfiguration ( ) { const config = { method : 'get' , url : `${ UserAPI . URL . CALLS }/${ UserAPI . URL . CONFIG }` , } ; const response = await this . client . sendJSON < RTCConfiguration > ( config ) ; return response . data ; } param userId param clientId see https public async getClient ( userId , clientId , useFederation ? ) ; public async getClient ( userId , clientId , useFederation ) ; public async getClient ( userId , clientId , useFederation = false ) { const url = useFederation && typeof userId !== 'string' ? `${ UserAPI . URL . USERS }/${ userId . domain }/${ userId . id }/${ UserAPI . URL . CLIENTS }/${ clientId }` : `${ UserAPI . URL . USERS }/${ userId }/${ UserAPI . URL . CLIENTS }/${ clientId }` ; const config = { method : 'get' , url , } ; const response = await this . client . sendJSON < PublicClient > ( config ) ; return response . data ; } param userId param clientId param useFederation see https public async getClientPreKey ( userId , clientId , useFederation ? ) ; public async getClientPreKey ( userId , clientId , useFederation ) ; public async getClientPreKey ( userId , clientId , useFederation = false ) { if ( useFederation && typeof userId !== 'string' ) { return this . getClientPreKey_v2 ( userId , clientId ) ; } return this . getClientPreKey_v1 ( userId as string , clientId ) ; } private async getClientPreKey_v1 ( userId , clientId ) { const url = `${ UserAPI . URL . USERS }/${ userId }/${ UserAPI . URL . PRE_KEYS }/${ clientId }` ; const config = { method : 'get' , url , } ; const response = await this . client . sendJSON < ClientPreKey > ( config ) ; return response . data ; } private async getClientPreKey_v2 ( userId , clientId ) { const { id , domain } = userId ; const url = `${ UserAPI . URL . USERS }/${ domain }/${ id }/${ UserAPI . URL . PRE_KEYS }/${ clientId }` ; const config = { method : 'get' , url , } ; const response = await this . client . sendJSON < ClientPreKey > ( config ) ; return response . data ; } param userId see https public async getClients ( userId , useFederation ? ) ; public async getClients ( userId , useFederation ) ; public async getClients ( userId , useFederation = false ) { const url = useFederation && typeof userId !== 'string' ? `${ UserAPI . URL . USERS }/${ userId . domain }/${ userId . id }/${ UserAPI . URL . CLIENTS }` : `${ UserAPI . URL . USERS }/${ userId }/${ UserAPI . URL . CLIENTS }` ; const config = { method : 'get' , url , } ; const response = await this . client . sendJSON < PublicClient [ ] > ( config ) ; return response . data ; } param handle see https public async getHandle ( handle ) { const config = { method : 'get' , url : `${ UserAPI . URL . USERS }/${ UserAPI . URL . HANDLES }/${ handle }` , } ; const response = await this . client . sendJSON < HandleInfo > ( config ) ; return response . data ; } see https public async getProperties ( ) { const config = { method : 'get' , url : UserAPI . URL . PROPERTIES , } ; const response = await this . client . sendJSON < string [ ] > ( config ) ; return response . data ; } param propertyKey see https public async getProperty < T > ( propertyKey ) { const config = { method : 'get' , url : `${ UserAPI . URL . PROPERTIES }/${ propertyKey }` , } ; const response = await this . client . sendJSON < T > ( config ) ; return response . data ; } param query param domain param limit see https public async getSearchContacts ( query , limit ? , domain ? ) { const cancelSource = Axios . CancelToken . source ( ) ; const config = { cancelToken : cancelSource . token , method : 'get' , params : { q : query , } , url : `${ UserAPI . URL . SEARCH }/${ UserAPI . URL . CONTACTS }` , } ; if ( domain ) { config . params . domain = domain ; } if ( limit ) { config . params . size = limit ; } const handleRequest = async ( ) => { try { const response = await this . client . sendJSON < SearchResult > ( config ) ; return response . data ; } catch ( error ) { if ( ( error as BackendError ) . message === SyntheticErrorLabel . REQUEST_CANCELLED ) { throw new RequestCancellationError ( 'Search request got cancelled' ) ; } throw error ; } } ; return { cancel : ( ) => cancelSource . cancel ( SyntheticErrorLabel . REQUEST_CANCELLED ) , response : handleRequest ( ) , } ; } note see https public async getUser ( userId ) { const url = typeof userId === 'string' ? `${ UserAPI . URL . USERS }/${ userId }` : `${ UserAPI . URL . USERS }/${ userId . domain }/${ userId . id }` ; const config = { method : 'get' , url , } ; const response = await this . client . sendJSON < User > ( config ) ; return response . data ; } public async getUserPreKeys ( userId ) { const url = typeof userId === 'string' ? `${ UserAPI . URL . USERS }/${ userId }/${ UserAPI . URL . PRE_KEYS }` : `${ UserAPI . URL . USERS }/${ userId . domain }/${ userId . id }/${ UserAPI . URL . PRE_KEYS }` ; const config = { method : 'get' , url , } ; const response = await this . client . sendJSON < PreKeyBundle > ( config , true ) ; return response . data ; } param parameters see https public async getUsers ( parameters , limit = UserAPI . DEFAULT_USERS_CHUNK_SIZE ) { const fetchUsers = async ( params ) : Promise < User [ ] > => { const config = { method : 'get' , params : { } , url : UserAPI . URL . USERS , } ; if ( 'handles' in params ) { config . params . handles = params . handles . join ( ',' ) ; } else if ( 'ids' in params ) { config . params . ids = params . ids . join ( ',' ) ; } const response = await this . client . sendJSON < User [ ] > ( config ) ; return response . data ; } ; if ( 'handles' in parameters && parameters . handles . length ) { const uniqueHandles = ArrayUtil . removeDuplicates ( parameters . handles ) ; const handleChunks = ArrayUtil . chunk ( uniqueHandles , limit ) ; const resolvedTasks = await Promise . all ( handleChunks . map ( handleChunk => fetchUsers ( { handles : handleChunk } ) ) ) ; return ArrayUtil . flatten ( resolvedTasks ) ; } if ( 'ids' in parameters && parameters . ids . length ) { const uniqueIds = ArrayUtil . removeDuplicates ( parameters . ids ) ; const idChunks = ArrayUtil . chunk ( uniqueIds , limit ) ; const resolvedTasks = await Promise . all ( idChunks . map ( idChunk => fetchUsers ( { ids : idChunk } ) ) ) ; return ArrayUtil . flatten ( resolvedTasks ) ; } return [ ] ; } param userIds see https public async getUsersByIds ( userIds ) { const maxChunkSize = 100 ; return this . getUsers ( { ids : userIds } , maxChunkSize ) ; } /**
     * Check if a user ID exists.
     */ public async headUsers ( userId ) { const url = typeof userId === 'string' ? `${ UserAPI . URL . USERS }/${ userId }` : `${ UserAPI . URL . USERS }/${ userId . domain }/${ userId . id }` ; const config = { method : 'head' , url , } ; await this . client . sendJSON ( config ) ; } param activationData see https public async postActivation ( activationData ) { const config = { data : activationData , method : 'post' , url : UserAPI . URL . ACTIVATE , } ; const response = await this . client . sendJSON < ActivationResponse > ( config ) ; return response . data ; } param activationCodeData see https public async postActivationCode ( activationCodeData ) { const config = { data : activationCodeData , method : 'post' , url : `${ UserAPI . URL . ACTIVATE }/${ UserAPI . URL . SEND }` , } ; await this . client . sendJSON ( config ) ; } param verificationData see https public async postDelete ( verificationData ) { const config = { data : verificationData , method : 'post' , url : UserAPI . URL . DELETE , } ; await this . client . sendJSON ( config ) ; } param handles see https public async postHandles ( handles ) { const config = { data : handles , method : 'post' , url : `${ UserAPI . URL . USERS }/${ UserAPI . URL . HANDLES }` , } ; const response = await this . client . sendJSON < string [ ] > ( config ) ; return response . data ; } param handle see https public async headHandle ( handle ) { const config = { method : 'head' , url : `${ UserAPI . URL . USERS }/${ UserAPI . URL . HANDLES }/${ handle }` , } ; await this . client . sendJSON ( config ) ; } param handle public async getUserByHandle ( handle ) { const config = { method : 'get' , url : `${ UserAPI . URL . USERS }/${ UserAPI . URL . BY_HANDLE }/${ handle . domain }/${ handle . handle }` , } ; const response = await this . client . sendJSON < User > ( config ) ; return response . data ; } private async postMultiPreKeyBundlesChunk ( userClientMap ) { const config = { data : userClientMap , method : 'post' , url : `${ UserAPI . URL . USERS }/${ UserAPI . URL . PRE_KEYS }` , } ; const response = await this . client . sendJSON < UserPreKeyBundleMap > ( config , true ) ; return response . data ; } private async postMultiQualifiedPreKeyBundlesChunk ( userClientMap ) { const config = { data : userClientMap , method : 'post' , url : `${ UserAPI . URL . USERS }/${ UserAPI . URL . LIST_PREKEYS }` , } ; const response = await this . client . sendJSON < QualifiedUserPreKeyBundleMap > ( config , true ) ; return response . data ; } /**
     * List users.
     * Note: The 'qualified_ids' and 'qualified_handles' parameters are mutually exclusive.
     */ public async postListUsers ( users ) { const config = { data : users , method : 'post' , url : UserAPI . URL . LIST_USERS , } ; const response = await this . client . sendJSON < User [ ] > ( config ) ; return response . data ; } /**
     * Get client infos from a list of users.
     */ public async postListClients ( userIdList ) { const config = { data : userIdList , method : 'post' , url : `${ UserAPI . URL . USERS }/${ UserAPI . URL . LIST_CLIENTS }/${ UserAPI . URL . V2 }` , } ; const response = await this . client . sendJSON < QualifiedPublicClients > ( config ) ; return response . data ; } param userClientMap see https public async postMultiPreKeyBundles ( userClientMap , limit = UserAPI . DEFAULT_USERS_PREKEY_BUNDLE_CHUNK_SIZE ) { const userIdChunks = ArrayUtil . chunk ( Object . keys ( userClientMap ) , limit ) ; const chunksPromises = userIdChunks . map ( userIdChunk => { const rebuiltMap = userIdChunk . reduce < UserClients > ( ( chunkedUserClientMap , userId ) => { chunkedUserClientMap [ userId ] = userClientMap [ userId ] ; return chunkedUserClientMap ; } , { } ) ; return this . postMultiPreKeyBundlesChunk ( rebuiltMap ) ; } ) ; const userPreKeyBundleMapChunks = await Promise . all ( chunksPromises ) ; return userPreKeyBundleMapChunks . reduce ( ( userPreKeyBundleMap , userPreKeyBundleMapChunk ) => { return { ... userPreKeyBundleMap , ... userPreKeyBundleMapChunk , } ; } , { } ) ; } param userClientMap public async postQualifiedMultiPreKeyBundles ( userClientMap , limit = UserAPI . DEFAULT_USERS_PREKEY_BUNDLE_CHUNK_SIZE ) { const domainChunks = ArrayUtil . chunk ( Object . keys ( userClientMap ) , limit ) ; const chunksPromises = domainChunks . map ( userIdChunk => { const rebuiltMap = userIdChunk . reduce < QualifiedUserClients > ( ( chunkedUserClientMap , domainOrUserId ) => { chunkedUserClientMap [ domainOrUserId ] = userClientMap [ domainOrUserId ] ; return chunkedUserClientMap ; } , { } ) ; return this . postMultiQualifiedPreKeyBundlesChunk ( rebuiltMap ) ; } ) ; const userPreKeyBundleMapChunks = await Promise . all ( chunksPromises ) ; return userPreKeyBundleMapChunks . reduce ( ( userPreKeyBundleMap , userPreKeyBundleMapChunk ) => { return { ... userPreKeyBundleMap , ... userPreKeyBundleMapChunk , } ; } , { } ) ; } param resetData see https see https public async postPasswordReset ( resetData ) { const config = { data : resetData , method : 'post' , url : UserAPI . URL . PASSWORD_RESET , } ; await this . client . sendJSON ( config ) ; } param propertyKey param propertyData see https public async putProperty < T > ( propertyKey , propertyData ) { const config = { data : propertyData , method : 'put' , url : `${ UserAPI . URL . PROPERTIES }/${ propertyKey }` , } ; await this . client . sendJSON ( config ) ; } param userId see https public async getRichInfo ( userId ) { const config = { method : 'get' , url : `${ UserAPI . URL . USERS }/${ userId }/${ UserAPI . URL . RICH_INFO }` , } ; const response = await this . client . sendJSON < RichInfo > ( config ) ; return response . data ; } param userId see https public async putRevalidateEmail ( userId , email ) { const config = { data : { email } , method : 'put' , url : `${ UserAPI . URL . USERS }/${ userId }/${ UserAPI . URL . EMAIL }` , } ; await this . client . sendJSON ( config ) ; } }