Files
twenty/packages/twenty-server/src/engine/api/graphql/graphql-config/hooks/use-cached-metadata.ts
T
Charles Bochet 5c745059ad refactor: remove "core" naming from views and eliminate converter layer (#18667)
## Summary

- **Remove all "core" prefixes** from the views system — the
metadata-based storage migration is complete, so `CoreView`,
`coreViewsSelector`, `getCoreViews`, etc. are now just `View`,
`viewsSelector`, `getViews`
- **Eliminate the entire converter layer** (15 files, ~850 lines
deleted) — `convertCoreViewToView` and all sub-converters were either
no-ops or trivially adding `__typename` / mapping identical enum values.
Local enums now re-export from generated GraphQL types directly (single
source of truth)
- **Unify `View` and `ViewWithRelations`** into one type —
`ViewWithRelations` is now a type alias for `View`, selectors return
data directly without conversion

### Backend
- Rename `@ObjectType('CoreView')` → `@ObjectType('View')` (and all
sub-entities)
- Rename resolver methods: `getCoreViews` → `getViews`, `createCoreView`
→ `createView`, etc.
- Rename `FIND_ALL_CORE_VIEWS_GRAPHQL_OPERATION` →
`FIND_ALL_VIEWS_GRAPHQL_OPERATION`

### Frontend
- Delete 15 converter files (`convertGqlView*ToView*`,
`convertView*ToGql`, `convertViewWithRelationsToView`)
- Re-export `ViewType`, `ViewKey`, `ViewFilterGroupLogicalOperator` from
generated enums (no more duplicate enum definitions with different
casing)
- Replace `ViewOpenRecordInType` with `ViewOpenRecordIn` from generated
- Remove `__typename` from all local view sub-types
- Remove unused `variant` from `ViewFilter`, make `displayValue` and
`definition` optional
- Rename ~45 GraphQL query/mutation files and all selectors to drop
"core" prefix
- Delete unused `viewsWithRelationsSelector`
2026-03-16 09:57:18 +01:00

103 lines
3.1 KiB
TypeScript

import { createHash } from 'crypto';
import { type Request } from 'express';
import { type Plugin } from 'graphql-yoga';
import { isDefined } from 'twenty-shared/utils';
import { InternalServerError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
export type CacheMetadataPluginConfig = {
// oxlint-disable-next-line @typescripttypescript/no-explicit-any
cacheGetter: (key: string) => any;
// oxlint-disable-next-line @typescripttypescript/no-explicit-any
cacheSetter: (key: string, value: any) => void;
operationsToCache: string[];
};
export function useCachedMetadata(config: CacheMetadataPluginConfig): Plugin {
const computeCacheKey = ({
operationName,
request,
}: {
operationName: string;
request: Pick<Request, 'workspace' | 'locale' | 'body' | 'userWorkspaceId'>;
}) => {
const workspace = request.workspace;
if (!isDefined(workspace)) {
throw new InternalServerError('Workspace is not defined');
}
const workspaceMetadataVersion = workspace.metadataVersion ?? '0';
const locale = request.locale;
const queryHash = createHash('sha256')
.update(request.body.query)
.digest('hex');
if (operationName === 'FindAllViews') {
return `graphql:operations:${operationName}:${workspace.id}:${workspaceMetadataVersion}:${request.userWorkspaceId}:${queryHash}`;
}
return `graphql:operations:${operationName}:${workspace.id}:${workspaceMetadataVersion}:${locale}:${queryHash}`;
};
// oxlint-disable-next-line @typescripttypescript/no-explicit-any
const getOperationName = (serverContext: any) =>
serverContext?.req?.body?.operationName;
return {
onRequest: async ({ endResponse, serverContext }) => {
// TODO: we should probably override the graphql-yoga request type to include the workspace and locale
const request = (serverContext as unknown as { req: Request }).req;
if (!request.workspace?.id) {
return;
}
if (!config.operationsToCache.includes(getOperationName(serverContext))) {
return;
}
const cacheKey = computeCacheKey({
operationName: getOperationName(serverContext),
request,
});
const cachedResponse = await config.cacheGetter(cacheKey);
if (cachedResponse) {
const earlyResponse = Response.json(cachedResponse);
return endResponse(earlyResponse);
}
},
onResponse: async ({ response, serverContext }) => {
const request = (serverContext as unknown as { req: Request }).req;
if (!request.workspace?.id) {
return;
}
if (!config.operationsToCache.includes(getOperationName(serverContext))) {
return;
}
const cacheKey = computeCacheKey({
operationName: getOperationName(serverContext),
request,
});
const cachedResponse = await config.cacheGetter(cacheKey);
if (!cachedResponse) {
const responseBody = await response.json();
if (responseBody.errors) {
return;
}
config.cacheSetter(cacheKey, responseBody);
}
},
};
}