diff --git a/src/libs/migrateOnyx.js b/src/libs/migrateOnyx.js index fd48ad1fa6eb..9389a9b66fbc 100644 --- a/src/libs/migrateOnyx.js +++ b/src/libs/migrateOnyx.js @@ -7,6 +7,7 @@ import MoveToIndexedDB from './migrations/MoveToIndexedDB'; import RenameExpensifyNewsStatus from './migrations/RenameExpensifyNewsStatus'; import AddLastVisibleActionCreated from './migrations/AddLastVisibleActionCreated'; import KeyReportActionsByReportActionID from './migrations/KeyReportActionsByReportActionID'; +import PersonalDetailsByAccountID from './migrations/PersonalDetailsByAccountID'; export default function () { const startTime = Date.now(); @@ -22,6 +23,7 @@ export default function () { RenameExpensifyNewsStatus, AddLastVisibleActionCreated, KeyReportActionsByReportActionID, + PersonalDetailsByAccountID, ]; // Reduce all promises down to a single promise. All promises run in a linear fashion, waiting for the diff --git a/src/libs/migrations/PersonalDetailsByAccountID.js b/src/libs/migrations/PersonalDetailsByAccountID.js new file mode 100644 index 000000000000..8716f063357e --- /dev/null +++ b/src/libs/migrations/PersonalDetailsByAccountID.js @@ -0,0 +1,213 @@ +import _ from 'underscore'; +import lodashHas from 'lodash/has'; +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '../../ONYXKEYS'; +import Log from '../Log'; + +const DEPRECATED_ONYX_KEYS = { + // Deprecated personal details object which was keyed by login instead of accountID. + PERSONAL_DETAILS: 'personalDetails', +}; + +/** + * @returns {Promise} + */ +function getReportActionsFromOnyx() { + return new Promise((resolve) => { + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + waitForCollectionCallback: true, + callback: (allReportActions) => { + Onyx.disconnect(connectionID); + return resolve(allReportActions); + }, + }); + }); +} + +/** + * We use the old personalDetails object becuase it is more efficient for this migration since it is keyed by email address. + * Also, if the old personalDetails object is not available, that likely means the migration has already run successfully before on this account. + * + * @returns {Promise} + */ +function getDeprecatedPersonalDetailsFromOnyx() { + return new Promise((resolve) => { + const connectionID = Onyx.connect({ + key: DEPRECATED_ONYX_KEYS.PERSONAL_DETAILS, + callback: (allPersonalDetails) => { + Onyx.disconnect(connectionID); + return resolve(allPersonalDetails); + }, + }); + }); +} + +/** + * Migrate Onyx data for the email to accountID migration. + * + * - reportAction_ + * - originalMessage.oldLogin -> originalMessage.oldAccountID + * - originalMessage.newLogin -> originalMessage.newAccountID + * - accountEmail -> accountID + * - actorEmail -> actorAccountID + * - childManagerEmail -> childManagerAccountID + * - whisperedTo -> whisperedToAccountIDs + * - childOldestFourEmails -> childOldestFourAccountIDs + * - originalMessage.participants -> originalMessage.participantAccountIDs + * + * @returns {Promise} + */ +export default function () { + return Promise.all([getReportActionsFromOnyx(), getDeprecatedPersonalDetailsFromOnyx()]).then(([oldReportActions, oldPersonalDetails]) => { + const onyxData = {}; + + if (!oldReportActions) { + Log.info('[Migrate Onyx] Skipped migration PersonalDetailsByAccountID because there were no reportActions'); + return; + } + + // We migrate reportActions to have the new accountID-based data if they don't already. + // If we are not able to get the accountID from personalDetails for some reason, we will just clear the reportAction + // and let it be fetched from the API next time they open the report and scroll to that action. + // We do this because we know the reportAction from the API will include the needed accountID data. + _.each(oldReportActions, (reportActionsForReport, onyxKey) => { + if (_.isEmpty(reportActionsForReport)) { + Log.info(`[Migrate Onyx] Skipped migration PersonalDetailsByAccountID for ${onyxKey} because there were no reportActions`); + return; + } + + const newReportActionsForReport = {}; + let reportActionsWereModified = false; + _.each(reportActionsForReport, (reportAction, reportActionID) => { + if (_.isEmpty(reportAction)) { + reportActionsWereModified = true; + Log.info(`[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction ${reportActionID} because the reportAction was empty`); + return; + } + + const newReportAction = reportAction; + + if (lodashHas(reportAction, ['originalMessage', 'oldLogin']) && !lodashHas(reportAction, ['originalMessage', 'oldAccountID'])) { + reportActionsWereModified = true; + const oldAccountID = _.get(oldPersonalDetails, [reportAction.originalMessage.oldLogin, 'accountID']); + if (oldAccountID) { + newReportAction.originalMessage.oldAccountID = oldAccountID; + } else { + Log.info(`[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction ${reportActionID} because originalMessage.oldAccountID not found`); + return; + } + } + + if (lodashHas(reportAction, ['originalMessage', 'newLogin']) && !lodashHas(reportAction, ['originalMessage', 'newAccountID'])) { + reportActionsWereModified = true; + const newAccountID = _.get(oldPersonalDetails, [reportAction.originalMessage.newLogin, 'accountID']); + if (newAccountID) { + newReportAction.originalMessage.newAccountID = newAccountID; + } else { + Log.info(`[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction ${reportActionID} because originalMessage.newAccountID not found`); + return; + } + } + + if (lodashHas(reportAction, ['accountEmail']) && !lodashHas(reportAction, ['accountID'])) { + reportActionsWereModified = true; + const accountID = _.get(oldPersonalDetails, [reportAction.accountEmail, 'accountID']); + if (accountID) { + newReportAction.accountID = accountID; + } else { + Log.info(`[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction ${reportActionID} because accountID not found`); + return; + } + } + + if (lodashHas(reportAction, ['actorEmail']) && !lodashHas(reportAction, ['actorAccountID'])) { + reportActionsWereModified = true; + const actorAccountID = _.get(oldPersonalDetails, [reportAction.actorEmail, 'accountID']); + if (actorAccountID) { + newReportAction.actorAccountID = actorAccountID; + } else { + Log.info(`[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction ${reportActionID} because actorAccountID not found`); + return; + } + } + + if (lodashHas(reportAction, ['childManagerEmail']) && !lodashHas(reportAction, ['childManagerAccountID'])) { + reportActionsWereModified = true; + const childManagerAccountID = _.get(oldPersonalDetails, [reportAction.childManagerEmail, 'accountID']); + if (childManagerAccountID) { + newReportAction.childManagerAccountID = childManagerAccountID; + } else { + Log.info(`[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction ${reportActionID} because childManagerAccountID not found`); + return; + } + } + + if (lodashHas(reportAction, ['whisperedTo']) && !lodashHas(reportAction, ['whisperedToAccountIDs'])) { + reportActionsWereModified = true; + const whisperedToAccountIDs = []; + _.each(reportAction.whisperedTo, (whisperedToLogin) => { + const whisperedToAccountID = _.get(oldPersonalDetails, [whisperedToLogin, 'accountID']); + if (whisperedToAccountID) { + whisperedToAccountIDs.push(whisperedToAccountID); + } + }); + + if (whisperedToAccountIDs.length === reportAction.whisperedTo.length) { + newReportAction.whisperedToAccountIDs = whisperedToAccountIDs; + } else { + Log.info(`[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction ${reportActionID} because whisperedToAccountIDs not found`); + return; + } + } + + if (lodashHas(reportAction, ['childOldestFourEmails']) && !lodashHas(reportAction, ['childOldestFourAccountIDs'])) { + reportActionsWereModified = true; + const childOldestFourEmails = reportAction.childOldestFourEmails.split(','); + const childOldestFourAccountIDs = []; + _.each(childOldestFourEmails, (login) => { + const accountID = _.get(oldPersonalDetails, [login.trim(), 'accountID']); + if (accountID) { + childOldestFourAccountIDs.push(accountID); + } + }); + + if (childOldestFourAccountIDs.length === childOldestFourEmails.length) { + newReportAction.childOldestFourAccountIDs = childOldestFourAccountIDs.join(','); + } else { + Log.info(`[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction ${reportActionID} because childOldestFourAccountIDs not found`); + return; + } + } + + if (lodashHas(reportAction, ['originalMessage', 'participants']) && !lodashHas(reportAction, ['originalMessage', 'participantAccountIDs'])) { + reportActionsWereModified = true; + const participantAccountIDs = []; + _.each(reportAction.originalMessage.participants, (participant) => { + const participantAccountID = _.get(oldPersonalDetails, [participant, 'accountID']); + if (participantAccountID) { + participantAccountIDs.push(participantAccountID); + } + }); + + if (participantAccountIDs.length === reportAction.originalMessage.participants.length) { + newReportAction.originalMessage.participantAccountIDs = participantAccountIDs; + } else { + Log.info(`[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction ${reportActionID} because originalMessage.participantAccountIDs not found`); + return; + } + } + + newReportActionsForReport[reportActionID] = newReportAction; + }); + + // Only include the reportActions from this report if at least one reportAction in this report + // was modified in any way. + if (reportActionsWereModified) { + onyxData[onyxKey] = newReportActionsForReport; + } + }); + + return Onyx.multiSet(onyxData); + }); +} diff --git a/tests/unit/MigrationTest.js b/tests/unit/MigrationTest.js index 41512b364646..e9f605699e43 100644 --- a/tests/unit/MigrationTest.js +++ b/tests/unit/MigrationTest.js @@ -7,6 +7,7 @@ import getPlatform from '../../src/libs/getPlatform'; import AddLastVisibleActionCreated from '../../src/libs/migrations/AddLastVisibleActionCreated'; import MoveToIndexedDB from '../../src/libs/migrations/MoveToIndexedDB'; import KeyReportActionsByReportActionID from '../../src/libs/migrations/KeyReportActionsByReportActionID'; +import PersonalDetailsByAccountID from '../../src/libs/migrations/PersonalDetailsByAccountID'; import ONYXKEYS from '../../src/ONYXKEYS'; jest.mock('../../src/libs/getPlatform'); @@ -253,4 +254,562 @@ describe('Migrations', () => { }); })); }); + + describe('PersonalDetailsByAccountID', () => { + const DEPRECATED_ONYX_KEYS = { + // Deprecated personal details object which was keyed by login instead of accountID. + PERSONAL_DETAILS: 'personalDetails', + }; + + // Warning: this test has to come before the others in this suite because Onyx.clear leaves traces and keys with null values aren't cleared out between tests + it("Should skip the migration if there's no reportAction data in Onyx", () => + PersonalDetailsByAccountID().then(() => expect(LogSpy).toHaveBeenCalledWith('[Migrate Onyx] Skipped migration PersonalDetailsByAccountID because there were no reportActions'))); + + it('Should skip any zombie reportAction collections that have no reportAction data in Onyx', () => + Onyx.multiSet({ + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: null, + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}2`]: null, + }) + .then(PersonalDetailsByAccountID) + .then(() => { + expect(LogSpy).toHaveBeenCalledWith( + `[Migrate Onyx] Skipped migration PersonalDetailsByAccountID for ${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1 because there were no reportActions`, + ); + expect(LogSpy).toHaveBeenCalledWith( + `[Migrate Onyx] Skipped migration PersonalDetailsByAccountID for ${ONYXKEYS.COLLECTION.REPORT_ACTIONS}2 because there were no reportActions`, + ); + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + waitForCollectionCallback: true, + callback: (allReportActions) => { + Onyx.disconnect(connectionID); + _.each(allReportActions, (reportActionsForReport) => expect(reportActionsForReport).toBeNull()); + }, + }); + })); + + it('Should remove any individual reportActions that have no data in Onyx', () => + Onyx.multiSet({ + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: { + 1: null, + 2: null, + }, + }) + .then(PersonalDetailsByAccountID) + .then(() => { + expect(LogSpy).toHaveBeenCalledWith('[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction 1 because the reportAction was empty'); + expect(LogSpy).toHaveBeenCalledWith('[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction 2 because the reportAction was empty'); + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + waitForCollectionCallback: true, + callback: (allReportActions) => { + Onyx.disconnect(connectionID); + _.each(allReportActions, (reportActionsForReport) => expect(reportActionsForReport).toMatchObject({})); + }, + }); + })); + + it('Should remove any individual reportActions that have originalMessage.oldLogin but the matching accountID is not found', () => + Onyx.multiSet({ + [`${DEPRECATED_ONYX_KEYS.PERSONAL_DETAILS}`]: { + 'test1@account.com': { + accountID: 100, + login: 'test1@account.com', + }, + 'test2@account.com': { + accountID: 101, + login: 'test2@account.com', + }, + }, + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: { + 1: { + reportActionID: '1', + originalMessage: { + oldLogin: 'test1@account.com', + newLogin: 'test2@account.com', + participants: ['test1@account.com', 'test2@account.com'], + }, + actorEmail: 'test2@account.com', + accountEmail: 'test2@account.com', + childManagerEmail: 'test2@account.com', + whisperedTo: ['test1@account.com', 'test2@account.com'], + childOldestFourEmails: 'test1@account.com, test2@account.com', + }, + 2: { + reportActionID: '2', + originalMessage: { + oldLogin: 'non-existent@account.com', + newLogin: 'test2@account.com', + participants: ['test1@account.com', 'test2@account.com'], + }, + actorEmail: 'test2@account.com', + accountEmail: 'test2@account.com', + childManagerEmail: 'test2@account.com', + whisperedTo: ['test1@account.com', 'test2@account.com'], + childOldestFourEmails: 'test1@account.com, test2@account.com', + }, + }, + }) + .then(PersonalDetailsByAccountID) + .then(() => { + expect(LogSpy).toHaveBeenCalledWith('[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction 2 because originalMessage.oldAccountID not found'); + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + waitForCollectionCallback: true, + callback: (allReportActions) => { + Onyx.disconnect(connectionID); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toHaveProperty('1'); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).not.toHaveProperty('2'); + }, + }); + })); + + it('Should remove any individual reportActions that have originalMessage.newLogin but the matching accountID is not found', () => + Onyx.multiSet({ + [`${DEPRECATED_ONYX_KEYS.PERSONAL_DETAILS}`]: { + 'test1@account.com': { + accountID: 100, + login: 'test1@account.com', + }, + 'test2@account.com': { + accountID: 101, + login: 'test2@account.com', + }, + }, + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: { + 1: { + reportActionID: '1', + originalMessage: { + oldLogin: 'test1@account.com', + newLogin: 'test2@account.com', + participants: ['test1@account.com', 'test2@account.com'], + }, + actorEmail: 'test2@account.com', + accountEmail: 'test2@account.com', + childManagerEmail: 'test2@account.com', + whisperedTo: ['test1@account.com', 'test2@account.com'], + childOldestFourEmails: 'test1@account.com, test2@account.com', + }, + 2: { + reportActionID: '2', + originalMessage: { + oldLogin: 'test1@account.com', + newLogin: 'non-existent@account.com', + participants: ['test1@account.com', 'test2@account.com'], + }, + actorEmail: 'test2@account.com', + accountEmail: 'test2@account.com', + childManagerEmail: 'test2@account.com', + whisperedTo: ['test1@account.com', 'test2@account.com'], + childOldestFourEmails: 'test1@account.com, test2@account.com', + }, + }, + }) + .then(PersonalDetailsByAccountID) + .then(() => { + expect(LogSpy).toHaveBeenCalledWith('[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction 2 because originalMessage.newAccountID not found'); + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + waitForCollectionCallback: true, + callback: (allReportActions) => { + Onyx.disconnect(connectionID); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toHaveProperty('1'); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).not.toHaveProperty('2'); + }, + }); + })); + + it('Should remove any individual reportActions that have accountEmail but the matching accountID is not found', () => + Onyx.multiSet({ + [`${DEPRECATED_ONYX_KEYS.PERSONAL_DETAILS}`]: { + 'test1@account.com': { + accountID: 100, + login: 'test1@account.com', + }, + 'test2@account.com': { + accountID: 101, + login: 'test2@account.com', + }, + }, + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: { + 1: { + reportActionID: '1', + originalMessage: { + oldLogin: 'test1@account.com', + newLogin: 'test2@account.com', + participants: ['test1@account.com', 'test2@account.com'], + }, + actorEmail: 'test2@account.com', + accountEmail: 'test2@account.com', + childManagerEmail: 'test2@account.com', + whisperedTo: ['test1@account.com', 'test2@account.com'], + childOldestFourEmails: 'test1@account.com, test2@account.com', + }, + 2: { + reportActionID: '2', + originalMessage: { + oldLogin: 'test1@account.com', + newLogin: 'test2@account.com', + participants: ['test1@account.com', 'test2@account.com'], + }, + actorEmail: 'test2@account.com', + accountEmail: 'non-existent@account.com', + childManagerEmail: 'test2@account.com', + whisperedTo: ['test1@account.com', 'test2@account.com'], + childOldestFourEmails: 'test1@account.com, test2@account.com', + }, + }, + }) + .then(PersonalDetailsByAccountID) + .then(() => { + expect(LogSpy).toHaveBeenCalledWith('[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction 2 because accountID not found'); + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + waitForCollectionCallback: true, + callback: (allReportActions) => { + Onyx.disconnect(connectionID); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toHaveProperty('1'); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).not.toHaveProperty('2'); + }, + }); + })); + + it('Should remove any individual reportActions that have actorEmail but the matching accountID is not found', () => + Onyx.multiSet({ + [`${DEPRECATED_ONYX_KEYS.PERSONAL_DETAILS}`]: { + 'test1@account.com': { + accountID: 100, + login: 'test1@account.com', + }, + 'test2@account.com': { + accountID: 101, + login: 'test2@account.com', + }, + }, + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: { + 1: { + reportActionID: '1', + originalMessage: { + oldLogin: 'test1@account.com', + newLogin: 'test2@account.com', + participants: ['test1@account.com', 'test2@account.com'], + }, + actorEmail: 'test2@account.com', + accountEmail: 'test2@account.com', + childManagerEmail: 'test2@account.com', + whisperedTo: ['test1@account.com', 'test2@account.com'], + childOldestFourEmails: 'test1@account.com, test2@account.com', + }, + 2: { + reportActionID: '2', + originalMessage: { + oldLogin: 'test1@account.com', + newLogin: 'test2@account.com', + participants: ['test1@account.com', 'test2@account.com'], + }, + actorEmail: 'non-existent@account.com', + accountEmail: 'test2@account.com', + childManagerEmail: 'test2@account.com', + whisperedTo: ['test1@account.com', 'test2@account.com'], + childOldestFourEmails: 'test1@account.com, test2@account.com', + }, + }, + }) + .then(PersonalDetailsByAccountID) + .then(() => { + expect(LogSpy).toHaveBeenCalledWith('[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction 2 because actorAccountID not found'); + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + waitForCollectionCallback: true, + callback: (allReportActions) => { + Onyx.disconnect(connectionID); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toHaveProperty('1'); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).not.toHaveProperty('2'); + }, + }); + })); + + it('Should remove any individual reportActions that have childManagerEmail but the matching accountID is not found', () => + Onyx.multiSet({ + [`${DEPRECATED_ONYX_KEYS.PERSONAL_DETAILS}`]: { + 'test1@account.com': { + accountID: 100, + login: 'test1@account.com', + }, + 'test2@account.com': { + accountID: 101, + login: 'test2@account.com', + }, + }, + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: { + 1: { + reportActionID: '1', + originalMessage: { + oldLogin: 'test1@account.com', + newLogin: 'test2@account.com', + participants: ['test1@account.com', 'test2@account.com'], + }, + actorEmail: 'test2@account.com', + accountEmail: 'test2@account.com', + childManagerEmail: 'test2@account.com', + whisperedTo: ['test1@account.com', 'test2@account.com'], + childOldestFourEmails: 'test1@account.com, test2@account.com', + }, + 2: { + reportActionID: '2', + originalMessage: { + oldLogin: 'test1@account.com', + newLogin: 'test2@account.com', + participants: ['test1@account.com', 'test2@account.com'], + }, + actorEmail: 'test2@account.com', + accountEmail: 'test2@account.com', + childManagerEmail: 'non-existent@account.com', + whisperedTo: ['test1@account.com', 'test2@account.com'], + childOldestFourEmails: 'test1@account.com, test2@account.com', + }, + }, + }) + .then(PersonalDetailsByAccountID) + .then(() => { + expect(LogSpy).toHaveBeenCalledWith('[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction 2 because childManagerAccountID not found'); + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + waitForCollectionCallback: true, + callback: (allReportActions) => { + Onyx.disconnect(connectionID); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toHaveProperty('1'); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).not.toHaveProperty('2'); + }, + }); + })); + + it('Should remove any individual reportActions that have whisperedTo but the matching accountID is not found', () => + Onyx.multiSet({ + [`${DEPRECATED_ONYX_KEYS.PERSONAL_DETAILS}`]: { + 'test1@account.com': { + accountID: 100, + login: 'test1@account.com', + }, + 'test2@account.com': { + accountID: 101, + login: 'test2@account.com', + }, + }, + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: { + 1: { + reportActionID: '1', + originalMessage: { + oldLogin: 'test1@account.com', + newLogin: 'test2@account.com', + participants: ['test1@account.com', 'test2@account.com'], + }, + actorEmail: 'test2@account.com', + accountEmail: 'test2@account.com', + childManagerEmail: 'test2@account.com', + whisperedTo: ['test1@account.com', 'test2@account.com'], + childOldestFourEmails: 'test1@account.com, test2@account.com', + }, + 2: { + reportActionID: '2', + originalMessage: { + oldLogin: 'test1@account.com', + newLogin: 'test2@account.com', + participants: ['test1@account.com', 'test2@account.com'], + }, + actorEmail: 'test2@account.com', + accountEmail: 'test2@account.com', + childManagerEmail: 'test2@account.com', + whisperedTo: ['test1@account.com', 'non-existent@account.com'], + childOldestFourEmails: 'test1@account.com, test2@account.com', + }, + }, + }) + .then(PersonalDetailsByAccountID) + .then(() => { + expect(LogSpy).toHaveBeenCalledWith('[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction 2 because whisperedToAccountIDs not found'); + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + waitForCollectionCallback: true, + callback: (allReportActions) => { + Onyx.disconnect(connectionID); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toHaveProperty('1'); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).not.toHaveProperty('2'); + }, + }); + })); + + it('Should remove any individual reportActions that have childOldestFourEmails but the matching accountID is not found', () => + Onyx.multiSet({ + [`${DEPRECATED_ONYX_KEYS.PERSONAL_DETAILS}`]: { + 'test1@account.com': { + accountID: 100, + login: 'test1@account.com', + }, + 'test2@account.com': { + accountID: 101, + login: 'test2@account.com', + }, + }, + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: { + 1: { + reportActionID: '1', + originalMessage: { + oldLogin: 'test1@account.com', + newLogin: 'test2@account.com', + participants: ['test1@account.com', 'test2@account.com'], + }, + actorEmail: 'test2@account.com', + accountEmail: 'test2@account.com', + childManagerEmail: 'test2@account.com', + whisperedTo: ['test1@account.com', 'test2@account.com'], + childOldestFourEmails: 'test1@account.com, test2@account.com', + }, + 2: { + reportActionID: '2', + originalMessage: { + oldLogin: 'test1@account.com', + newLogin: 'test2@account.com', + participants: ['test1@account.com', 'test2@account.com'], + }, + actorEmail: 'test2@account.com', + accountEmail: 'test2@account.com', + childManagerEmail: 'test2@account.com', + whisperedTo: ['test1@account.com', 'test2@account.com'], + childOldestFourEmails: 'test1@account.com, non-existent@account.com', + }, + }, + }) + .then(PersonalDetailsByAccountID) + .then(() => { + expect(LogSpy).toHaveBeenCalledWith('[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction 2 because childOldestFourAccountIDs not found'); + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + waitForCollectionCallback: true, + callback: (allReportActions) => { + Onyx.disconnect(connectionID); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toHaveProperty('1'); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).not.toHaveProperty('2'); + }, + }); + })); + + it('Should remove any individual reportActions that have originalMessage.participants but the matching accountID is not found', () => + Onyx.multiSet({ + [`${DEPRECATED_ONYX_KEYS.PERSONAL_DETAILS}`]: { + 'test1@account.com': { + accountID: 100, + login: 'test1@account.com', + }, + 'test2@account.com': { + accountID: 101, + login: 'test2@account.com', + }, + }, + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: { + 1: { + reportActionID: '1', + originalMessage: { + oldLogin: 'test1@account.com', + newLogin: 'test2@account.com', + participants: ['test1@account.com', 'test2@account.com'], + }, + actorEmail: 'test2@account.com', + accountEmail: 'test2@account.com', + childManagerEmail: 'test2@account.com', + whisperedTo: ['test1@account.com', 'test2@account.com'], + childOldestFourEmails: 'test1@account.com, test2@account.com', + }, + 2: { + reportActionID: '2', + originalMessage: { + oldLogin: 'test1@account.com', + newLogin: 'test2@account.com', + participants: ['test1@account.com', 'non-existent@account.com'], + }, + actorEmail: 'test2@account.com', + accountEmail: 'test2@account.com', + childManagerEmail: 'test2@account.com', + whisperedTo: ['test1@account.com', 'test2@account.com'], + childOldestFourEmails: 'test1@account.com, test2@account.com', + }, + }, + }) + .then(PersonalDetailsByAccountID) + .then(() => { + expect(LogSpy).toHaveBeenCalledWith( + '[Migrate Onyx] PersonalDetailsByAccountID migration: removing reportAction 2 because originalMessage.participantAccountIDs not found', + ); + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + waitForCollectionCallback: true, + callback: (allReportActions) => { + Onyx.disconnect(connectionID); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).toHaveProperty('1'); + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]).not.toHaveProperty('2'); + }, + }); + })); + + it('Should succeed in adding all relevant accountID data if it can be found in personalDetails', () => + Onyx.multiSet({ + [`${DEPRECATED_ONYX_KEYS.PERSONAL_DETAILS}`]: { + 'test1@account.com': { + accountID: 100, + login: 'test1@account.com', + }, + 'test2@account.com': { + accountID: 101, + login: 'test2@account.com', + }, + }, + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: { + 1: { + reportActionID: '1', + originalMessage: { + oldLogin: 'test1@account.com', + newLogin: 'test2@account.com', + participants: ['test1@account.com', 'test2@account.com'], + }, + actorEmail: 'test2@account.com', + accountEmail: 'test2@account.com', + childManagerEmail: 'test2@account.com', + whisperedTo: ['test1@account.com', 'test2@account.com'], + childOldestFourEmails: 'test1@account.com, test2@account.com', + }, + }, + }) + .then(PersonalDetailsByAccountID) + .then(() => { + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + waitForCollectionCallback: true, + callback: (allReportActions) => { + Onyx.disconnect(connectionID); + const expectedReportAction = { + reportActionID: '1', + originalMessage: { + oldLogin: 'test1@account.com', + oldAccountID: 100, + newLogin: 'test2@account.com', + newAccountID: 101, + participants: ['test1@account.com', 'test2@account.com'], + participantAccountIDs: [100, 101], + }, + actorEmail: 'test2@account.com', + actorAccountID: 101, + accountEmail: 'test2@account.com', + accountID: 101, + childManagerEmail: 'test2@account.com', + childManagerAccountID: 101, + whisperedTo: ['test1@account.com', 'test2@account.com'], + whisperedToAccountIDs: [100, 101], + childOldestFourEmails: 'test1@account.com, test2@account.com', + childOldestFourAccountIDs: '100,101', + }; + expect(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`][1]).toMatchObject(expectedReportAction); + }, + }); + })); + }); });