diff --git a/lib/utils/conversion-utils.js b/lib/utils/conversion-utils.js index ed52540fd..368241729 100644 --- a/lib/utils/conversion-utils.js +++ b/lib/utils/conversion-utils.js @@ -1,177 +1,179 @@ // @flow import _mapKeys from 'lodash/fp/mapKeys.js'; import _mapValues from 'lodash/fp/mapValues.js'; import type { TInterface, TType } from 'tcomb'; import { convertIDToNewSchema } from './migration-utils.js'; import { assertWithValidator, tID, tUserID } from './validation-utils.js'; import { getPendingThreadID, parsePendingThreadID, } from '../shared/thread-utils.js'; function convertServerIDsToClientIDs( serverPrefixID: string, outputValidator: TType, data: T, ): T { const conversionFunction = (id: string) => { if (id.indexOf('|') !== -1) { console.warn(`Server id '${id}' already has a prefix`); return id; } return convertIDToNewSchema(id, serverPrefixID); }; return convertObject(outputValidator, data, [tID], conversionFunction); } function convertClientIDsToServerIDs( serverPrefixID: string, outputValidator: TType, data: T, ): T { const prefix = serverPrefixID + '|'; const conversionFunction = (id: string) => { if (id.startsWith(prefix)) { return id.substr(prefix.length); } const pendingIDContents = parsePendingThreadID(id); if (!pendingIDContents) { throw new Error('invalid_client_id_prefix'); } if (!pendingIDContents.sourceMessageID) { return id; } return getPendingThreadID( pendingIDContents.threadType, pendingIDContents.memberIDs, pendingIDContents.sourceMessageID.substr(prefix.length), ); }; return convertObject(outputValidator, data, [tID], conversionFunction); } function extractUserIDsFromPayload( outputValidator: TType, data: T, ): $ReadOnlyArray { const result = new Set(); const conversionFunction = (id: string) => { result.add(id); return id; }; - convertObject(outputValidator, data, [tUserID], conversionFunction); + try { + convertObject(outputValidator, data, [tUserID], conversionFunction); + } catch {} return [...result]; } function convertObject( validator: TType, input: I, typesToConvert: $ReadOnlyArray>, conversionFunction: T => T, ): I { if (input === null || input === undefined) { return input; } // While they should be the same runtime object, // `TValidator` is `TType` and `validator` is `TType`. // Having them have different types allows us to use `assertWithValidator` // to change `input` flow type const TValidator = typesToConvert[typesToConvert.indexOf(validator)]; if (TValidator && TValidator.is(input)) { const TInput = assertWithValidator(input, TValidator); const converted = conversionFunction(TInput); return assertWithValidator(converted, validator); } if (validator.meta.kind === 'maybe' || validator.meta.kind === 'subtype') { return convertObject( validator.meta.type, input, typesToConvert, conversionFunction, ); } if (validator.meta.kind === 'interface' && typeof input === 'object') { const recastValidator: TInterface = (validator: any); const result: { [string]: mixed } = {}; for (const key in input) { const innerValidator = recastValidator.meta.props[key]; result[key] = convertObject( innerValidator, input[key], typesToConvert, conversionFunction, ); } return assertWithValidator(result, recastValidator); } if (validator.meta.kind === 'union') { for (const innerValidator of validator.meta.types) { if (innerValidator.is(input)) { return convertObject( innerValidator, input, typesToConvert, conversionFunction, ); } } return input; } if (validator.meta.kind === 'list' && Array.isArray(input)) { const innerValidator = validator.meta.type; return (input.map(value => convertObject(innerValidator, value, typesToConvert, conversionFunction), ): any); } if (validator.meta.kind === 'dict' && typeof input === 'object') { const domainValidator = validator.meta.domain; const codomainValidator = validator.meta.codomain; if (typesToConvert.includes(domainValidator)) { input = _mapKeys(key => conversionFunction(key))(input); } return _mapValues(value => convertObject( codomainValidator, value, typesToConvert, conversionFunction, ), )(input); } return input; } // NOTE: This function should not be called from native. On native, we should // use `convertObjToBytes` in native/backup/conversion-utils.js instead. function convertObjToBytes(obj: T): Uint8Array { const objStr = JSON.stringify(obj); return new TextEncoder().encode(objStr ?? ''); } // NOTE: This function should not be called from native. On native, we should // use `convertBytesToObj` in native/backup/conversion-utils.js instead. function convertBytesToObj(bytes: Uint8Array): T { const str = new TextDecoder().decode(bytes.buffer); return JSON.parse(str); } export { convertClientIDsToServerIDs, convertServerIDsToClientIDs, extractUserIDsFromPayload, convertObject, convertObjToBytes, convertBytesToObj, };