Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3369673
D9442.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
21 KB
Referenced Files
None
Subscribers
None
D9442.diff
View Options
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<BadDeviceTokenUpdateInfo> =
- 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<AccountDeletionUpdateInfo> =
- 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<ThreadDeletionUpdateInfo> =
- 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<string>,
- update: ThreadDeletionUpdateInfo,
- ) {
- if (!filteredThreadIDs.has(update.threadID)) {
- return filteredThreadIDs;
- }
- return new Set(
- [...filteredThreadIDs].filter(id => id !== update.threadID),
- );
- },
- });
+ },
+ ];
+ }
+ return null;
+ },
+ reduceCalendarThreadFilters(
+ filteredThreadIDs: $ReadOnlySet<string>,
+ 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<ThreadJoinUpdateInfo> = 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<CurrentUserUpdateInfo> =
- 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<EntryUpdateInfo> = Object.freeze({
- mergeEntryInfos(
- entryIDs: Set<string>,
- mergedEntryInfos: Array<RawEntryInfo>,
- 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<EntryUpdateInfo, EntryRawUpdateInfo> =
+ Object.freeze({
+ mergeEntryInfos(
+ entryIDs: Set<string>,
+ mergedEntryInfos: Array<RawEntryInfo>,
+ 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<UpdateInfo: ClientUpdateInfo> = {
+export type UpdateSpec<UpdateInfo: ClientUpdateInfo, RawInfo: RawUpdateInfo> = {
+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<ThreadReadStatusUpdateInfo> =
- 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<ThreadUpdateInfo> = 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<UserUpdateInfo> = Object.freeze({});
+export const updateUserSpec: UpdateSpec<UserUpdateInfo, UserRawUpdateInfo> =
+ 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,
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Nov 26, 11:09 PM (21 h, 25 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2586802
Default Alt Text
D9442.diff (21 KB)
Attached To
Mode
D9442: [lib] Move transforming rows into updates to specs
Attached
Detach File
Event Timeline
Log In to Comment