Skip to content

Commit deb54b5

Browse files
committed
chore: update cache functionality to use nestjs-cache module
1 parent d74d9f1 commit deb54b5

File tree

10 files changed

+264
-241
lines changed

10 files changed

+264
-241
lines changed

api/generated-schema.graphql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1245,7 +1245,7 @@ type DockerNetwork {
12451245

12461246
type Docker implements Node {
12471247
id: ID!
1248-
containers: [DockerContainer!]!
1248+
containers(useCache: Boolean! = true): [DockerContainer!]!
12491249
networks: [DockerNetwork!]!
12501250
}
12511251

api/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"@graphql-tools/utils": "^10.5.5",
6464
"@jsonforms/core": "^3.5.1",
6565
"@nestjs/apollo": "^13.0.3",
66+
"@nestjs/cache-manager": "^3.0.1",
6667
"@nestjs/common": "^11.0.11",
6768
"@nestjs/config": "^4.0.2",
6869
"@nestjs/core": "^11.0.11",
@@ -78,6 +79,7 @@
7879
"accesscontrol": "^2.2.1",
7980
"bycontract": "^2.0.11",
8081
"bytes": "^3.1.2",
82+
"cache-manager": "^6.4.2",
8183
"cacheable-lookup": "^7.0.0",
8284
"camelcase-keys": "^9.1.3",
8385
"casbin": "^5.32.0",

api/src/unraid-api/app/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Module } from '@nestjs/common';
22
import { APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core';
33
import { ThrottlerModule } from '@nestjs/throttler';
4+
import { CacheModule } from '@nestjs/cache-manager';
45

56
import { AuthZGuard } from 'nest-authz';
67
import { LoggerModule } from 'nestjs-pino';
@@ -38,6 +39,7 @@ import { UnraidFileModifierModule } from '@app/unraid-api/unraid-file-modifier/u
3839
}),
3940
AuthModule,
4041
CronModule,
42+
CacheModule.register(),
4143
GraphModule,
4244
RestModule,
4345
ThrottlerModule.forRoot([

api/src/unraid-api/graph/resolvers/docker/docker-event.service.spec.ts

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,28 @@ vi.mock('@app/store/index.js', () => ({
3939
},
4040
}));
4141

42+
// Mock pubsub
43+
vi.mock('@app/core/pubsub.js', () => ({
44+
pubsub: {
45+
publish: vi.fn().mockResolvedValue(undefined),
46+
},
47+
PUBSUB_CHANNEL: {
48+
INFO: 'info',
49+
},
50+
}));
51+
4252
// Mock DockerService
4353
vi.mock('./docker.service.js', () => ({
4454
DockerService: vi.fn().mockImplementation(() => ({
4555
getDockerClient: vi.fn(),
46-
debouncedContainerCacheUpdate: vi.fn(),
56+
clearContainerCache: vi.fn(),
57+
getAppInfo: vi.fn().mockResolvedValue({ info: { apps: { installed: 1, running: 1 } } }),
4758
})),
4859
}));
4960

61+
// Import pubsub for use in tests
62+
import { pubsub, PUBSUB_CHANNEL } from '@app/core/pubsub.js';
63+
5064
describe('DockerEventService', () => {
5165
let service: DockerEventService;
5266
let dockerService: DockerService;
@@ -64,7 +78,8 @@ describe('DockerEventService', () => {
6478
// Create a mock Docker service *instance*
6579
const mockDockerServiceImpl = {
6680
getDockerClient: vi.fn().mockReturnValue(mockDockerClient),
67-
debouncedContainerCacheUpdate: vi.fn(),
81+
clearContainerCache: vi.fn(),
82+
getAppInfo: vi.fn().mockResolvedValue({ info: { apps: { installed: 1, running: 1 } } }),
6883
};
6984

7085
// Create a mock event stream
@@ -124,7 +139,9 @@ describe('DockerEventService', () => {
124139

125140
await waitForEventProcessing();
126141

127-
expect(dockerService.debouncedContainerCacheUpdate).toHaveBeenCalled();
142+
expect(dockerService.clearContainerCache).toHaveBeenCalled();
143+
expect(dockerService.getAppInfo).toHaveBeenCalled();
144+
expect(pubsub.publish).toHaveBeenCalledWith(PUBSUB_CHANNEL.INFO, expect.any(Object));
128145
});
129146

130147
it('should ignore non-watched actions', async () => {
@@ -144,7 +161,9 @@ describe('DockerEventService', () => {
144161

145162
await waitForEventProcessing();
146163

147-
expect(dockerService.debouncedContainerCacheUpdate).not.toHaveBeenCalled();
164+
expect(dockerService.clearContainerCache).not.toHaveBeenCalled();
165+
expect(dockerService.getAppInfo).not.toHaveBeenCalled();
166+
expect(pubsub.publish).not.toHaveBeenCalled();
148167
});
149168

150169
it('should handle malformed JSON gracefully', async () => {
@@ -160,7 +179,9 @@ describe('DockerEventService', () => {
160179
await waitForEventProcessing();
161180

162181
expect(service.isActive()).toBe(true);
163-
expect(dockerService.debouncedContainerCacheUpdate).toHaveBeenCalledTimes(1);
182+
expect(dockerService.clearContainerCache).toHaveBeenCalledTimes(1);
183+
expect(dockerService.getAppInfo).toHaveBeenCalledTimes(1);
184+
expect(pubsub.publish).toHaveBeenCalledTimes(1);
164185
});
165186

166187
it('should handle multiple JSON bodies in a single chunk', async () => {
@@ -176,7 +197,9 @@ describe('DockerEventService', () => {
176197

177198
await waitForEventProcessing();
178199

179-
expect(dockerService.debouncedContainerCacheUpdate).toHaveBeenCalledTimes(2);
200+
expect(dockerService.clearContainerCache).toHaveBeenCalledTimes(2);
201+
expect(dockerService.getAppInfo).toHaveBeenCalledTimes(2);
202+
expect(pubsub.publish).toHaveBeenCalledTimes(2);
180203
});
181204

182205
it('should handle mixed valid and invalid JSON in a single chunk', async () => {
@@ -190,7 +213,9 @@ describe('DockerEventService', () => {
190213

191214
await waitForEventProcessing();
192215

193-
expect(dockerService.debouncedContainerCacheUpdate).toHaveBeenCalledTimes(1);
216+
expect(dockerService.clearContainerCache).toHaveBeenCalledTimes(1);
217+
expect(dockerService.getAppInfo).toHaveBeenCalledTimes(1);
218+
expect(pubsub.publish).toHaveBeenCalledTimes(1);
194219

195220
expect(service.isActive()).toBe(true);
196221
});
@@ -205,7 +230,9 @@ describe('DockerEventService', () => {
205230

206231
await waitForEventProcessing();
207232

208-
expect(dockerService.debouncedContainerCacheUpdate).toHaveBeenCalledTimes(1);
233+
expect(dockerService.clearContainerCache).toHaveBeenCalledTimes(1);
234+
expect(dockerService.getAppInfo).toHaveBeenCalledTimes(1);
235+
expect(pubsub.publish).toHaveBeenCalledTimes(1);
209236

210237
expect(service.isActive()).toBe(true);
211238
});

api/src/unraid-api/graph/resolvers/docker/docker-event.service.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Docker from 'dockerode';
66

77
import { getters } from '@app/store/index.js';
88
import { DockerService } from '@app/unraid-api/graph/resolvers/docker/docker.service.js';
9+
import { pubsub, PUBSUB_CHANNEL } from '@app/core/pubsub.js';
910

1011
enum DockerEventAction {
1112
DIE = 'die',
@@ -128,7 +129,11 @@ export class DockerEventService implements OnModuleDestroy, OnModuleInit {
128129
typeof actionName === 'string' &&
129130
this.containerActions.includes(actionName as DockerEventAction)
130131
) {
131-
await this.dockerService.debouncedContainerCacheUpdate();
132+
await this.dockerService.clearContainerCache();
133+
// Get updated counts and publish
134+
const appInfo = await this.dockerService.getAppInfo();
135+
await pubsub.publish(PUBSUB_CHANNEL.INFO, appInfo);
136+
this.logger.debug(`Published app info update due to event: ${actionName}`);
132137
}
133138
}
134139
}

api/src/unraid-api/graph/resolvers/docker/docker.resolver.spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ describe('DockerResolver', () => {
1919
provide: DockerService,
2020
useValue: {
2121
getContainers: vi.fn(),
22+
getNetworks: vi.fn(),
2223
},
2324
},
2425
],
@@ -66,8 +67,8 @@ describe('DockerResolver', () => {
6667
];
6768
vi.mocked(dockerService.getContainers).mockResolvedValue(mockContainers);
6869

69-
const result = await resolver.containers();
70+
const result = await resolver.containers(false);
7071
expect(result).toEqual(mockContainers);
71-
expect(dockerService.getContainers).toHaveBeenCalledWith({ useCache: false });
72+
expect(dockerService.getContainers).toHaveBeenCalledWith({ skipCache: false });
7273
});
7374
});

api/src/unraid-api/graph/resolvers/docker/docker.resolver.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Query, ResolveField, Resolver } from '@nestjs/graphql';
1+
import { Args, Query, ResolveField, Resolver } from '@nestjs/graphql';
22

33
import {
44
AuthActionVerb,
@@ -35,8 +35,8 @@ export class DockerResolver {
3535
possession: AuthPossession.ANY,
3636
})
3737
@ResolveField(() => [DockerContainer])
38-
public async containers() {
39-
return this.dockerService.getContainers({ useCache: false });
38+
public async containers(@Args('skipCache', { defaultValue: false, type: () => Boolean }) skipCache: boolean) {
39+
return this.dockerService.getContainers({ skipCache });
4040
}
4141

4242
@UsePermissions({
@@ -45,7 +45,7 @@ export class DockerResolver {
4545
possession: AuthPossession.ANY,
4646
})
4747
@ResolveField(() => [DockerNetwork])
48-
public async networks() {
49-
return this.dockerService.getNetworks({ useCache: false });
48+
public async networks(@Args('skipCache', { defaultValue: false, type: () => Boolean }) skipCache: boolean) {
49+
return this.dockerService.getNetworks({ skipCache });
5050
}
5151
}

0 commit comments

Comments
 (0)