diff --git a/keyserver/src/fetchers/update-fetchers.js b/keyserver/src/fetchers/update-fetchers.js --- a/keyserver/src/fetchers/update-fetchers.js +++ b/keyserver/src/fetchers/update-fetchers.js @@ -1,7 +1,6 @@ // @flow -import invariant from 'invariant'; - +import { updateSpecs } from 'lib/shared/updates/update-specs.js'; import type { CalendarQuery } from 'lib/types/entry-types.js'; import { updateTypes, assertUpdateType } from 'lib/types/update-types-enum.js'; import { type RawUpdateInfo } from 'lib/types/update-types.js'; @@ -52,83 +51,9 @@ return fetchUpdateInfosWithQuery({ viewer, calendarQuery }, query); } -// ESLint doesn't recognize that invariant always throws -// eslint-disable-next-line consistent-return function rawUpdateInfoFromRow(row: Object): RawUpdateInfo { const type = assertUpdateType(row.type); - if (type === updateTypes.DELETE_ACCOUNT) { - const content = JSON.parse(row.content); - return { - type: updateTypes.DELETE_ACCOUNT, - id: row.id.toString(), - time: row.time, - deletedUserID: content.deletedUserID, - }; - } else if (type === updateTypes.UPDATE_THREAD) { - const { threadID } = JSON.parse(row.content); - return { - type: updateTypes.UPDATE_THREAD, - id: row.id.toString(), - time: row.time, - threadID, - }; - } else if (type === updateTypes.UPDATE_THREAD_READ_STATUS) { - const { threadID, unread } = JSON.parse(row.content); - return { - type: updateTypes.UPDATE_THREAD_READ_STATUS, - id: row.id.toString(), - time: row.time, - threadID, - unread, - }; - } else if (type === updateTypes.DELETE_THREAD) { - const { threadID } = JSON.parse(row.content); - return { - type: updateTypes.DELETE_THREAD, - id: row.id.toString(), - time: row.time, - threadID, - }; - } else if (type === updateTypes.JOIN_THREAD) { - const { threadID } = JSON.parse(row.content); - return { - type: updateTypes.JOIN_THREAD, - id: row.id.toString(), - time: row.time, - threadID, - }; - } else if (type === updateTypes.BAD_DEVICE_TOKEN) { - const { deviceToken } = JSON.parse(row.content); - return { - type: updateTypes.BAD_DEVICE_TOKEN, - id: row.id.toString(), - time: row.time, - deviceToken, - }; - } else if (type === updateTypes.UPDATE_ENTRY) { - const { entryID } = JSON.parse(row.content); - return { - type: updateTypes.UPDATE_ENTRY, - id: row.id.toString(), - time: row.time, - entryID, - }; - } else if (type === updateTypes.UPDATE_CURRENT_USER) { - return { - type: updateTypes.UPDATE_CURRENT_USER, - id: row.id.toString(), - time: row.time, - }; - } else if (type === updateTypes.UPDATE_USER) { - const content = JSON.parse(row.content); - return { - type: updateTypes.UPDATE_USER, - id: row.id.toString(), - time: row.time, - updatedUserID: content.updatedUserID, - }; - } - invariant(false, `unrecognized updateType ${type}`); + return updateSpecs[type].rawUpdateInfoFromRow(row); } const entryIDExtractString = '$.entryID'; diff --git a/lib/shared/updates/bad-device-token-spec.js b/lib/shared/updates/bad-device-token-spec.js --- a/lib/shared/updates/bad-device-token-spec.js +++ b/lib/shared/updates/bad-device-token-spec.js @@ -1,7 +1,23 @@ // @flow import type { UpdateSpec } from './update-spec.js'; -import type { BadDeviceTokenUpdateInfo } from '../../types/update-types.js'; +import { updateTypes } from '../../types/update-types-enum.js'; +import type { + BadDeviceTokenRawUpdateInfo, + BadDeviceTokenUpdateInfo, +} from '../../types/update-types.js'; -export const badDeviceTokenSpec: UpdateSpec = - Object.freeze({}); +export const badDeviceTokenSpec: UpdateSpec< + BadDeviceTokenUpdateInfo, + BadDeviceTokenRawUpdateInfo, +> = Object.freeze({ + rawUpdateInfoFromRow(row: Object) { + const { deviceToken } = JSON.parse(row.content); + return { + type: updateTypes.BAD_DEVICE_TOKEN, + id: row.id.toString(), + time: row.time, + deviceToken, + }; + }, +}); diff --git a/lib/shared/updates/delete-account-spec.js b/lib/shared/updates/delete-account-spec.js --- a/lib/shared/updates/delete-account-spec.js +++ b/lib/shared/updates/delete-account-spec.js @@ -2,43 +2,58 @@ import type { UpdateSpec } from './update-spec.js'; import type { RawThreadInfos } from '../../types/thread-types.js'; -import type { AccountDeletionUpdateInfo } from '../../types/update-types.js'; +import { updateTypes } from '../../types/update-types-enum.js'; +import type { + AccountDeletionRawUpdateInfo, + AccountDeletionUpdateInfo, +} from '../../types/update-types.js'; import type { UserInfos } from '../../types/user-types.js'; -export const deleteAccountSpec: UpdateSpec = - Object.freeze({ - generateOpsForThreadUpdates( - storeThreadInfos: RawThreadInfos, - update: AccountDeletionUpdateInfo, - ) { - const operations = []; - for (const threadID in storeThreadInfos) { - const threadInfo = storeThreadInfos[threadID]; - const newMembers = threadInfo.members.filter( - member => member.id !== update.deletedUserID, - ); - if (newMembers.length < threadInfo.members.length) { - const updatedThread = { - ...threadInfo, - members: newMembers, - }; - operations.push({ - type: 'replace', - payload: { - id: threadID, - threadInfo: updatedThread, - }, - }); - } +export const deleteAccountSpec: UpdateSpec< + AccountDeletionUpdateInfo, + AccountDeletionRawUpdateInfo, +> = Object.freeze({ + generateOpsForThreadUpdates( + storeThreadInfos: RawThreadInfos, + update: AccountDeletionUpdateInfo, + ) { + const operations = []; + for (const threadID in storeThreadInfos) { + const threadInfo = storeThreadInfos[threadID]; + const newMembers = threadInfo.members.filter( + member => member.id !== update.deletedUserID, + ); + if (newMembers.length < threadInfo.members.length) { + const updatedThread = { + ...threadInfo, + members: newMembers, + }; + operations.push({ + type: 'replace', + payload: { + id: threadID, + threadInfo: updatedThread, + }, + }); } - return operations; - }, - reduceUserInfos(state: UserInfos, update: AccountDeletionUpdateInfo) { - const { deletedUserID } = update; - if (!state[deletedUserID]) { - return state; - } - const { [deletedUserID]: deleted, ...rest } = state; - return rest; - }, - }); + } + return operations; + }, + reduceUserInfos(state: UserInfos, update: AccountDeletionUpdateInfo) { + const { deletedUserID } = update; + if (!state[deletedUserID]) { + return state; + } + const { [deletedUserID]: deleted, ...rest } = state; + return rest; + }, + rawUpdateInfoFromRow(row: Object) { + const content = JSON.parse(row.content); + return { + type: updateTypes.DELETE_ACCOUNT, + id: row.id.toString(), + time: row.time, + deletedUserID: content.deletedUserID, + }; + }, +}); diff --git a/lib/shared/updates/delete-thread-spec.js b/lib/shared/updates/delete-thread-spec.js --- a/lib/shared/updates/delete-thread-spec.js +++ b/lib/shared/updates/delete-thread-spec.js @@ -2,35 +2,48 @@ import type { UpdateSpec } from './update-spec.js'; import type { RawThreadInfos } from '../../types/thread-types.js'; -import type { ThreadDeletionUpdateInfo } from '../../types/update-types.js'; +import { updateTypes } from '../../types/update-types-enum.js'; +import type { + ThreadDeletionRawUpdateInfo, + ThreadDeletionUpdateInfo, +} from '../../types/update-types.js'; -export const deleteThreadSpec: UpdateSpec = - Object.freeze({ - generateOpsForThreadUpdates( - storeThreadInfos: RawThreadInfos, - update: ThreadDeletionUpdateInfo, - ) { - if (storeThreadInfos[update.threadID]) { - return [ - { - type: 'remove', - payload: { - ids: [update.threadID], - }, +export const deleteThreadSpec: UpdateSpec< + ThreadDeletionUpdateInfo, + ThreadDeletionRawUpdateInfo, +> = Object.freeze({ + generateOpsForThreadUpdates( + storeThreadInfos: RawThreadInfos, + update: ThreadDeletionUpdateInfo, + ) { + if (storeThreadInfos[update.threadID]) { + return [ + { + type: 'remove', + payload: { + ids: [update.threadID], }, - ]; - } - return null; - }, - reduceCalendarThreadFilters( - filteredThreadIDs: $ReadOnlySet, - update: ThreadDeletionUpdateInfo, - ) { - if (!filteredThreadIDs.has(update.threadID)) { - return filteredThreadIDs; - } - return new Set( - [...filteredThreadIDs].filter(id => id !== update.threadID), - ); - }, - }); + }, + ]; + } + return null; + }, + reduceCalendarThreadFilters( + filteredThreadIDs: $ReadOnlySet, + update: ThreadDeletionUpdateInfo, + ) { + if (!filteredThreadIDs.has(update.threadID)) { + return filteredThreadIDs; + } + return new Set([...filteredThreadIDs].filter(id => id !== update.threadID)); + }, + rawUpdateInfoFromRow(row: Object) { + const { threadID } = JSON.parse(row.content); + return { + type: updateTypes.DELETE_THREAD, + id: row.id.toString(), + time: row.time, + threadID, + }; + }, +}); diff --git a/lib/shared/updates/join-thread-spec.js b/lib/shared/updates/join-thread-spec.js --- a/lib/shared/updates/join-thread-spec.js +++ b/lib/shared/updates/join-thread-spec.js @@ -9,11 +9,18 @@ MessageTruncationStatuses, } from '../../types/message-types.js'; import type { RawThreadInfos } from '../../types/thread-types.js'; -import type { ThreadJoinUpdateInfo } from '../../types/update-types.js'; +import { updateTypes } from '../../types/update-types-enum.js'; +import type { + ThreadJoinUpdateInfo, + ThreadJoinRawUpdateInfo, +} from '../../types/update-types.js'; import { combineTruncationStatuses } from '../message-utils.js'; import { threadInFilterList } from '../thread-utils.js'; -export const joinThreadSpec: UpdateSpec = Object.freeze({ +export const joinThreadSpec: UpdateSpec< + ThreadJoinUpdateInfo, + ThreadJoinRawUpdateInfo, +> = Object.freeze({ generateOpsForThreadUpdates( storeThreadInfos: RawThreadInfos, update: ThreadJoinUpdateInfo, @@ -80,4 +87,13 @@ truncationStatuses[update.threadInfo.id], ); }, + rawUpdateInfoFromRow(row: Object) { + const { threadID } = JSON.parse(row.content); + return { + type: updateTypes.JOIN_THREAD, + id: row.id.toString(), + time: row.time, + threadID, + }; + }, }); diff --git a/lib/shared/updates/update-current-user-spec.js b/lib/shared/updates/update-current-user-spec.js --- a/lib/shared/updates/update-current-user-spec.js +++ b/lib/shared/updates/update-current-user-spec.js @@ -3,15 +3,28 @@ import _isEqual from 'lodash/fp/isEqual.js'; import type { UpdateSpec } from './update-spec.js'; -import type { CurrentUserUpdateInfo } from '../../types/update-types.js'; +import { updateTypes } from '../../types/update-types-enum.js'; +import type { + CurrentUserUpdateInfo, + CurrentUserRawUpdateInfo, +} from '../../types/update-types.js'; import type { CurrentUserInfo } from '../../types/user-types.js'; -export const updateCurrentUserSpec: UpdateSpec = - Object.freeze({ - reduceCurrentUser(state: ?CurrentUserInfo, update: CurrentUserUpdateInfo) { - if (!_isEqual(update.currentUserInfo)(state)) { - return update.currentUserInfo; - } - return state; - }, - }); +export const updateCurrentUserSpec: UpdateSpec< + CurrentUserUpdateInfo, + CurrentUserRawUpdateInfo, +> = Object.freeze({ + reduceCurrentUser(state: ?CurrentUserInfo, update: CurrentUserUpdateInfo) { + if (!_isEqual(update.currentUserInfo)(state)) { + return update.currentUserInfo; + } + return state; + }, + rawUpdateInfoFromRow(row: Object) { + return { + type: updateTypes.UPDATE_CURRENT_USER, + id: row.id.toString(), + time: row.time, + }; + }, +}); diff --git a/lib/shared/updates/update-entry-spec.js b/lib/shared/updates/update-entry-spec.js --- a/lib/shared/updates/update-entry-spec.js +++ b/lib/shared/updates/update-entry-spec.js @@ -2,20 +2,34 @@ import type { UpdateSpec } from './update-spec.js'; import type { RawEntryInfo } from '../../types/entry-types.js'; -import type { EntryUpdateInfo } from '../../types/update-types.js'; +import { updateTypes } from '../../types/update-types-enum.js'; +import type { + EntryUpdateInfo, + EntryRawUpdateInfo, +} from '../../types/update-types.js'; -export const updateEntrySpec: UpdateSpec = Object.freeze({ - mergeEntryInfos( - entryIDs: Set, - mergedEntryInfos: Array, - update: EntryUpdateInfo, - ) { - const { entryInfo } = update; - const entryID = entryInfo.id; - if (!entryID || entryIDs.has(entryID)) { - return; - } - mergedEntryInfos.push(entryInfo); - entryIDs.add(entryID); - }, -}); +export const updateEntrySpec: UpdateSpec = + Object.freeze({ + mergeEntryInfos( + entryIDs: Set, + mergedEntryInfos: Array, + update: EntryUpdateInfo, + ) { + const { entryInfo } = update; + const entryID = entryInfo.id; + if (!entryID || entryIDs.has(entryID)) { + return; + } + mergedEntryInfos.push(entryInfo); + entryIDs.add(entryID); + }, + rawUpdateInfoFromRow(row: Object) { + const { entryID } = JSON.parse(row.content); + return { + type: updateTypes.UPDATE_ENTRY, + id: row.id.toString(), + time: row.time, + entryID, + }; + }, + }); diff --git a/lib/shared/updates/update-spec.js b/lib/shared/updates/update-spec.js --- a/lib/shared/updates/update-spec.js +++ b/lib/shared/updates/update-spec.js @@ -7,10 +7,13 @@ MessageTruncationStatuses, } from '../../types/message-types.js'; import type { RawThreadInfos } from '../../types/thread-types.js'; -import type { ClientUpdateInfo } from '../../types/update-types.js'; +import type { + ClientUpdateInfo, + RawUpdateInfo, +} from '../../types/update-types.js'; import type { CurrentUserInfo, UserInfos } from '../../types/user-types.js'; -export type UpdateSpec = { +export type UpdateSpec = { +generateOpsForThreadUpdates?: ( storeThreadInfos: RawThreadInfos, update: UpdateInfo, @@ -36,4 +39,5 @@ truncationStatuses: MessageTruncationStatuses, update: UpdateInfo, ) => void, + +rawUpdateInfoFromRow: (row: Object) => RawInfo, }; diff --git a/lib/shared/updates/update-specs.js b/lib/shared/updates/update-specs.js --- a/lib/shared/updates/update-specs.js +++ b/lib/shared/updates/update-specs.js @@ -13,7 +13,7 @@ import { updateTypes, type UpdateType } from '../../types/update-types-enum.js'; export const updateSpecs: { - +[UpdateType]: UpdateSpec<*>, + +[UpdateType]: UpdateSpec<*, *>, } = Object.freeze({ [updateTypes.DELETE_ACCOUNT]: deleteAccountSpec, [updateTypes.UPDATE_THREAD]: updateThreadSpec, diff --git a/lib/shared/updates/update-thread-read-status-spec.js b/lib/shared/updates/update-thread-read-status-spec.js --- a/lib/shared/updates/update-thread-read-status-spec.js +++ b/lib/shared/updates/update-thread-read-status-spec.js @@ -5,36 +5,52 @@ RawThreadInfo, RawThreadInfos, } from '../../types/thread-types.js'; -import type { ThreadReadStatusUpdateInfo } from '../../types/update-types.js'; +import { updateTypes } from '../../types/update-types-enum.js'; +import type { + ThreadReadStatusUpdateInfo, + ThreadReadStatusRawUpdateInfo, +} from '../../types/update-types.js'; -export const updateThreadReadStatusSpec: UpdateSpec = - Object.freeze({ - generateOpsForThreadUpdates( - storeThreadInfos: RawThreadInfos, - update: ThreadReadStatusUpdateInfo, +export const updateThreadReadStatusSpec: UpdateSpec< + ThreadReadStatusUpdateInfo, + ThreadReadStatusRawUpdateInfo, +> = Object.freeze({ + generateOpsForThreadUpdates( + storeThreadInfos: RawThreadInfos, + update: ThreadReadStatusUpdateInfo, + ) { + const storeThreadInfo: ?RawThreadInfo = storeThreadInfos[update.threadID]; + if ( + !storeThreadInfo || + storeThreadInfo.currentUser.unread === update.unread ) { - const storeThreadInfo: ?RawThreadInfo = storeThreadInfos[update.threadID]; - if ( - !storeThreadInfo || - storeThreadInfo.currentUser.unread === update.unread - ) { - return null; - } - const updatedThread = { - ...storeThreadInfo, - currentUser: { - ...storeThreadInfo.currentUser, - unread: update.unread, - }, - }; - return [ - { - type: 'replace', - payload: { - id: update.threadID, - threadInfo: updatedThread, - }, + return null; + } + const updatedThread = { + ...storeThreadInfo, + currentUser: { + ...storeThreadInfo.currentUser, + unread: update.unread, + }, + }; + return [ + { + type: 'replace', + payload: { + id: update.threadID, + threadInfo: updatedThread, }, - ]; - }, - }); + }, + ]; + }, + rawUpdateInfoFromRow(row: Object) { + const { threadID, unread } = JSON.parse(row.content); + return { + type: updateTypes.UPDATE_THREAD_READ_STATUS, + id: row.id.toString(), + time: row.time, + threadID, + unread, + }; + }, +}); diff --git a/lib/shared/updates/update-thread-spec.js b/lib/shared/updates/update-thread-spec.js --- a/lib/shared/updates/update-thread-spec.js +++ b/lib/shared/updates/update-thread-spec.js @@ -4,10 +4,17 @@ import type { UpdateSpec } from './update-spec.js'; import type { RawThreadInfos } from '../../types/thread-types.js'; -import type { ThreadUpdateInfo } from '../../types/update-types.js'; +import { updateTypes } from '../../types/update-types-enum.js'; +import type { + ThreadUpdateInfo, + ThreadRawUpdateInfo, +} from '../../types/update-types.js'; import { threadInFilterList } from '../thread-utils.js'; -export const updateThreadSpec: UpdateSpec = Object.freeze({ +export const updateThreadSpec: UpdateSpec< + ThreadUpdateInfo, + ThreadRawUpdateInfo, +> = Object.freeze({ generateOpsForThreadUpdates( storeThreadInfos: RawThreadInfos, update: ThreadUpdateInfo, @@ -39,4 +46,13 @@ [...filteredThreadIDs].filter(id => id !== update.threadInfo.id), ); }, + rawUpdateInfoFromRow(row: Object) { + const { threadID } = JSON.parse(row.content); + return { + type: updateTypes.UPDATE_THREAD, + id: row.id.toString(), + time: row.time, + threadID, + }; + }, }); diff --git a/lib/shared/updates/update-user-spec.js b/lib/shared/updates/update-user-spec.js --- a/lib/shared/updates/update-user-spec.js +++ b/lib/shared/updates/update-user-spec.js @@ -1,6 +1,21 @@ // @flow import type { UpdateSpec } from './update-spec.js'; -import type { UserUpdateInfo } from '../../types/update-types.js'; +import { updateTypes } from '../../types/update-types-enum.js'; +import type { + UserUpdateInfo, + UserRawUpdateInfo, +} from '../../types/update-types.js'; -export const updateUserSpec: UpdateSpec = Object.freeze({}); +export const updateUserSpec: UpdateSpec = + Object.freeze({ + rawUpdateInfoFromRow(row: Object) { + const content = JSON.parse(row.content); + return { + type: updateTypes.UPDATE_USER, + id: row.id.toString(), + time: row.time, + updatedUserID: content.updatedUserID, + }; + }, + }); diff --git a/lib/types/update-types.js b/lib/types/update-types.js --- a/lib/types/update-types.js +++ b/lib/types/update-types.js @@ -118,47 +118,47 @@ +id: string, +time: number, }; -type AccountDeletionRawUpdateInfo = { +export type AccountDeletionRawUpdateInfo = { ...SharedRawUpdateInfo, ...AccountDeletionData, +type: 0, }; -type ThreadRawUpdateInfo = { +export type ThreadRawUpdateInfo = { ...SharedRawUpdateInfo, ...ThreadData, +type: 1, }; -type ThreadReadStatusRawUpdateInfo = { +export type ThreadReadStatusRawUpdateInfo = { ...SharedRawUpdateInfo, ...ThreadReadStatusData, +type: 2, }; -type ThreadDeletionRawUpdateInfo = { +export type ThreadDeletionRawUpdateInfo = { ...SharedRawUpdateInfo, ...ThreadDeletionData, +type: 3, }; -type ThreadJoinRawUpdateInfo = { +export type ThreadJoinRawUpdateInfo = { ...SharedRawUpdateInfo, ...ThreadJoinData, +type: 4, }; -type BadDeviceTokenRawUpdateInfo = { +export type BadDeviceTokenRawUpdateInfo = { ...SharedRawUpdateInfo, ...BadDeviceTokenData, +type: 5, }; -type EntryRawUpdateInfo = { +export type EntryRawUpdateInfo = { ...SharedRawUpdateInfo, ...EntryData, +type: 6, }; -type CurrentUserRawUpdateInfo = { +export type CurrentUserRawUpdateInfo = { ...SharedRawUpdateInfo, ...CurrentUserData, +type: 7, }; -type UserRawUpdateInfo = { +export type UserRawUpdateInfo = { ...SharedRawUpdateInfo, ...UserData, +type: 8,