UNPKG

react-native-avo-inspector

Version:

[![npm version](https://badge.fury.io/js/react-native-avo-inspector.svg)](https://badge.fury.io/js/react-native-avo-inspector)

129 lines (128 loc) 5.34 kB
const isArray = (obj) => { return Object.prototype.toString.call(obj) === "[object Array]"; }; let _encryptValueFn; export class AvoSchemaParser { /** * Returns true only if we have a valid encryption key and can send encrypted values. * If no key is present, returns false and no property values will be sent. */ static canSendEncryptedValues(publicEncryptionKey, env) { const hasEncryptionKey = publicEncryptionKey != null && publicEncryptionKey !== ""; const isDevOrStaging = env === "dev" || env === "staging"; return hasEncryptionKey && isDevOrStaging; } /** * Returns the encrypted property value if encryption is enabled, otherwise undefined. * Never returns unencrypted values - only encrypted or nothing. */ static async getEncryptedPropertyValueIfEnabled(propertyValue, canEncrypt, publicEncryptionKey) { if (!canEncrypt || !publicEncryptionKey) { return undefined; // No encryption key: do not send any property values } try { if (!_encryptValueFn) { const { encryptValue } = await import("./AvoEncryption"); _encryptValueFn = encryptValue; } return await _encryptValueFn(propertyValue, publicEncryptionKey); // Only send encrypted values } catch (error) { // If encryption fails, log the error but don't fail the entire schema extraction console.error("[Avo Inspector] Failed to encrypt property value:", error instanceof Error ? error.message : String(error)); return undefined; } } static async extractSchema(eventProperties, publicEncryptionKey, env) { if (eventProperties === null || eventProperties === undefined) { return []; } const canSendEncryptedValues = this.canSendEncryptedValues(publicEncryptionKey, env); const mapping = async (object) => { if (isArray(object)) { const list = await Promise.all(object.map(async (x) => { return await mapping(x); })); return this.removeDuplicates(list); } else if (typeof object === "object") { const mappedResult = []; for (const key in object) { if (object.hasOwnProperty(key)) { const val = object[key]; const mappedEntry = { propertyName: key, propertyType: this.getPropValueType(val) }; if (typeof val === "object" && val != null) { // Object/array properties: children are encrypted individually, no need to encrypt parent mappedEntry.children = (await mapping(val)); } else if (val !== undefined) { // Primitive properties: encrypt the value if encryption is enabled // Skip undefined values - they can't be encrypted and shouldn't be sent const encryptedValue = await this.getEncryptedPropertyValueIfEnabled(val, canSendEncryptedValues, publicEncryptionKey); if (encryptedValue !== undefined) { mappedEntry.encryptedPropertyValue = encryptedValue; } } mappedResult.push(mappedEntry); } } return mappedResult; } else { return this.getPropValueType(object); } }; // eventProperties is always an object (Record), so mapping returns EventProperty[] const mappedEventProps = (await mapping(eventProperties)); return mappedEventProps; } static removeDuplicates(array) { const primitives = { boolean: {}, number: {}, string: {} }; const objects = []; return array.filter((item) => { const type = typeof item; if (type in primitives && typeof item === "string") { return primitives[type].hasOwnProperty(item) ? false : (primitives[type][item] = true); } else { return objects.includes(item) ? false : objects.push(item); } }); } static getPropValueType(propValue) { const propType = typeof propValue; if (propValue == null) { return "null"; } else if (propType === "string") { return "string"; } else if (propType === "number" || propType === "bigint") { if ((propValue + "").includes(".")) { return "float"; } else { return "int"; } } else if (propType === "boolean") { return "boolean"; } else if (propType === "object") { if (isArray(propValue)) { return "list"; } else { return "object"; } } else { return "unknown"; } } }