d5a7dec117
## Summary - Renames `ObjectMetadataItem` to `EnrichedObjectMetadataItem` across the entire frontend (~440 files) to clarify that this type includes derived fields (`readableFields`, `updatableFields`, nested `fields[]`, `indexMetadatas[]`) computed at read time from the metadata store - Creates `splitObjectMetadataGqlResponse` that goes directly from a GraphQL `ObjectMetadataItemsQuery` response to flat store items (combining the old `mapPaginatedObjectMetadataItemsToObjectMetadataItems` + `splitObjectMetadataItemWithRelated` two-step flow into one call) - Removes `ObjectMetadataItemWithRelated` type and all "WithRelated" naming - Renames `generatedMockObjectMetadataItems` to `generateTestEnrichedObjectMetadataItemsMock` to make it clear this is test-only enriched data - Deletes `useLoadMockedObjectMetadataItems` hook (consolidated into `useLoadMockedMinimalMetadata`) - Ensures nothing destined for the metadata store computes `readableFields`/`updatableFields` (preventing the localStorage bloat from #18809) ## Type hierarchy (before → after) **Before:** ``` ObjectMetadataItemsQuery → mapPaginated → ObjectMetadataItemWithRelated → enrich → ObjectMetadataItem → split → FlatObjectMetadataItem (store) ``` **After:** ``` ObjectMetadataItemsQuery → splitObjectMetadataGqlResponse → FlatObjectMetadataItem (store) → mapPaginated + enrich (tests only) → EnrichedObjectMetadataItem ``` ## Test plan - [x] `npx nx typecheck twenty-front` passes - [x] `npx nx test twenty-front` passes (767 suites, 4505 tests) - [x] `npx nx lint twenty-front` passes - [ ] CI checks pass Made with [Cursor](https://cursor.com)
119 lines
3.6 KiB
TypeScript
119 lines
3.6 KiB
TypeScript
import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem';
|
|
import { getRecordFromCache } from '@/object-record/cache/utils/getRecordFromCache';
|
|
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
|
import { generateDepthRecordGqlFieldsFromRecord } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromRecord';
|
|
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
|
import { InMemoryCache, type NormalizedCacheObject } from '@apollo/client';
|
|
|
|
import { isDefined } from 'twenty-shared/utils';
|
|
|
|
type ObjectMetadataItemAndRecordId = {
|
|
recordId: string;
|
|
objectMetadataItem: EnrichedObjectMetadataItem;
|
|
};
|
|
type RecordsWithObjectMetadataItem = {
|
|
records: ObjectRecord[];
|
|
objectMetadataItem: EnrichedObjectMetadataItem;
|
|
}[];
|
|
|
|
type GetMockCachedRecord = {
|
|
recordId: string;
|
|
objectMetadataItem: EnrichedObjectMetadataItem;
|
|
matchObject?: Record<string, unknown>;
|
|
snapshotPropertyMatchers?: {
|
|
deletedAt?: any;
|
|
updatedAt?: any;
|
|
createdAt?: any;
|
|
};
|
|
};
|
|
type InMemoryTestingCacheInstanceArgs = {
|
|
objectMetadataItems: EnrichedObjectMetadataItem[];
|
|
initialRecordsInCache?: RecordsWithObjectMetadataItem;
|
|
};
|
|
|
|
export class InMemoryTestingCacheInstance {
|
|
private _cache: InMemoryCache;
|
|
private objectMetadataItems: EnrichedObjectMetadataItem[];
|
|
private initialStateExtract: NormalizedCacheObject;
|
|
|
|
constructor({
|
|
objectMetadataItems,
|
|
initialRecordsInCache = [],
|
|
}: InMemoryTestingCacheInstanceArgs) {
|
|
this.objectMetadataItems = objectMetadataItems;
|
|
this._cache = new InMemoryCache();
|
|
|
|
this.populateRecordsInCache(initialRecordsInCache);
|
|
this.initialStateExtract = this._cache.extract();
|
|
}
|
|
|
|
public populateRecordsInCache = (
|
|
recordsWithObjectMetadataItem: RecordsWithObjectMetadataItem,
|
|
) => {
|
|
recordsWithObjectMetadataItem.forEach(({ objectMetadataItem, records }) =>
|
|
records.forEach((record) =>
|
|
updateRecordFromCache({
|
|
cache: this._cache,
|
|
objectMetadataItem,
|
|
objectMetadataItems: this.objectMetadataItems,
|
|
record,
|
|
recordGqlFields: generateDepthRecordGqlFieldsFromRecord({
|
|
objectMetadataItems: this.objectMetadataItems,
|
|
depth: 1,
|
|
objectMetadataItem,
|
|
record,
|
|
}),
|
|
objectPermissionsByObjectMetadataId: {},
|
|
}),
|
|
),
|
|
);
|
|
};
|
|
|
|
public assertCachedRecordIsNull = ({
|
|
objectMetadataItem,
|
|
recordId,
|
|
}: ObjectMetadataItemAndRecordId) => {
|
|
const cachedRecord = getRecordFromCache({
|
|
cache: this._cache,
|
|
objectMetadataItem,
|
|
objectMetadataItems: this.objectMetadataItems,
|
|
recordId,
|
|
objectPermissionsByObjectMetadataId: {},
|
|
});
|
|
expect(cachedRecord).toBeNull();
|
|
};
|
|
|
|
public assertCachedRecordMatchSnapshot = ({
|
|
objectMetadataItem,
|
|
recordId,
|
|
matchObject,
|
|
snapshotPropertyMatchers,
|
|
}: GetMockCachedRecord) => {
|
|
const cachedRecord = getRecordFromCache({
|
|
cache: this._cache,
|
|
objectMetadataItem,
|
|
objectMetadataItems: this.objectMetadataItems,
|
|
recordId,
|
|
objectPermissionsByObjectMetadataId: {},
|
|
});
|
|
expect(cachedRecord).not.toBeNull();
|
|
|
|
if (cachedRecord === null) {
|
|
throw new Error('Should never occurs, cachedRecord is null');
|
|
}
|
|
|
|
if (isDefined(matchObject)) {
|
|
expect(cachedRecord).toMatchObject(matchObject);
|
|
}
|
|
expect(cachedRecord).toMatchSnapshot(snapshotPropertyMatchers ?? {});
|
|
};
|
|
|
|
public restoreCacheToInitialState = async () => {
|
|
return this._cache.restore(this.initialStateExtract);
|
|
};
|
|
|
|
public get cache() {
|
|
return this._cache;
|
|
}
|
|
}
|