refactor: Introduce granular query types with specific policies for improved caching and data freshness.

This commit is contained in:
pa
2026-03-11 14:37:43 +09:00
parent 14d73b1532
commit a75c4b89f8
23 changed files with 221 additions and 39 deletions

View File

@@ -161,6 +161,59 @@ describe('queryRequest', () => {
expect(args.json.id).toBe('usr_1');
});
test('uses same queryKey for user and user.dialog callers', async () => {
const data = { json: { id: 'usr_1' }, params: { userId: 'usr_1' } };
mockGetUser.mockResolvedValue(data);
mockFetchWithEntityPolicy.mockImplementation(async ({ queryFn }) => ({
data: await queryFn(),
cache: false
}));
await queryRequest.fetch('user', { userId: 'usr_1' });
await queryRequest.fetch('user.dialog', { userId: 'usr_1' });
const baseCall = mockFetchWithEntityPolicy.mock.calls[0][0];
const dialogCall = mockFetchWithEntityPolicy.mock.calls[1][0];
expect(baseCall.queryKey).toEqual(['user', 'usr_1']);
expect(dialogCall.queryKey).toEqual(baseCall.queryKey);
});
test('applies staleTime zero for user.force', async () => {
const data = { json: { id: 'usr_2' }, params: { userId: 'usr_2' } };
mockGetUser.mockResolvedValue(data);
mockFetchWithEntityPolicy.mockImplementation(async ({ queryFn }) => ({
data: await queryFn(),
cache: false
}));
await queryRequest.fetch('user.force', { userId: 'usr_2' });
expect(mockFetchWithEntityPolicy).toHaveBeenCalledWith(
expect.objectContaining({
policy: expect.objectContaining({ staleTime: 0 }),
label: 'user.force'
})
);
});
test('applies staleTime 60000 for user.dialog', async () => {
const data = { json: { id: 'usr_3' }, params: { userId: 'usr_3' } };
mockGetUser.mockResolvedValue(data);
mockFetchWithEntityPolicy.mockImplementation(async ({ queryFn }) => ({
data: await queryFn(),
cache: false
}));
await queryRequest.fetch('user.dialog', { userId: 'usr_3' });
expect(mockFetchWithEntityPolicy).toHaveBeenCalledWith(
expect.objectContaining({
policy: expect.objectContaining({ staleTime: 60_000 }),
label: 'user.dialog'
})
);
});
test('supports worldsByUser option routing', async () => {
const params = {
userId: 'usr_me',
@@ -214,4 +267,11 @@ describe('queryRequest', () => {
queryRequest.fetch('missing_resource', {})
).rejects.toThrow('Unknown query resource');
});
test('throws on unknown caller variant', async () => {
await expect(
// @ts-expect-error verifying runtime guard
queryRequest.fetch('user.unknown', { userId: 'usr_1' })
).rejects.toThrow('Unknown query resource: user.unknown');
});
});

View File

@@ -21,16 +21,64 @@ const registry = Object.freeze({
policy: entityQueryPolicies.user,
queryFn: (params) => userRequest.getUser(params)
},
'user.dialog': {
key: (params) => queryKeys.user(params.userId),
policy: Object.freeze({
...entityQueryPolicies.user,
staleTime: 60_000
}),
queryFn: (params) => userRequest.getUser(params)
},
'user.force': {
key: (params) => queryKeys.user(params.userId),
policy: Object.freeze({
...entityQueryPolicies.user,
staleTime: 0
}),
queryFn: (params) => userRequest.getUser(params)
},
avatar: {
key: (params) => queryKeys.avatar(params.avatarId),
policy: entityQueryPolicies.avatar,
queryFn: (params) => avatarRequest.getAvatar(params)
},
'avatar.dialog': {
key: (params) => queryKeys.avatar(params.avatarId),
policy: Object.freeze({
...entityQueryPolicies.avatar,
staleTime: 120_000
}),
queryFn: (params) => avatarRequest.getAvatar(params)
},
world: {
key: (params) => queryKeys.world(params.worldId),
policy: entityQueryPolicies.world,
queryFn: (params) => worldRequest.getWorld(params)
},
'world.dialog': {
key: (params) => queryKeys.world(params.worldId),
policy: Object.freeze({
...entityQueryPolicies.world,
staleTime: 120_000
}),
queryFn: (params) => worldRequest.getWorld(params)
},
'world.location': {
key: (params) => queryKeys.world(params.worldId),
policy: Object.freeze({
...entityQueryPolicies.world,
staleTime: 120_000
}),
queryFn: (params) => worldRequest.getWorld(params)
},
'world.force': {
key: (params) => queryKeys.world(params.worldId),
policy: Object.freeze({
...entityQueryPolicies.world,
staleTime: 0
}),
queryFn: (params) => worldRequest.getWorld(params)
},
worldsByUser: {
key: (params) => queryKeys.worldsByUser(params),
policy: entityQueryPolicies.worldCollection,
@@ -42,6 +90,22 @@ const registry = Object.freeze({
policy: entityQueryPolicies.group,
queryFn: (params) => groupRequest.getGroup(params)
},
'group.dialog': {
key: (params) => queryKeys.group(params.groupId, params.includeRoles),
policy: Object.freeze({
...entityQueryPolicies.group,
staleTime: 120_000
}),
queryFn: (params) => groupRequest.getGroup(params)
},
'group.force': {
key: (params) => queryKeys.group(params.groupId, params.includeRoles),
policy: Object.freeze({
...entityQueryPolicies.group,
staleTime: 0
}),
queryFn: (params) => groupRequest.getGroup(params)
},
groupMember: {
key: (params) => queryKeys.groupMember(params),
policy: entityQueryPolicies.groupCollection,
@@ -135,7 +199,8 @@ const queryRequest = {
const { data, cache } = await fetchWithEntityPolicy({
queryKey: entry.key(params),
policy: entry.policy,
queryFn: () => entry.queryFn(params)
queryFn: () => entry.queryFn(params),
label: resource
});
return {