Skip to content

Commit a3168b5

Browse files
committed
refactor: integrate GraphQLFieldHelper for field request handling in DockerResolver
- Replaced the custom isFieldRequested method with GraphQLFieldHelper to streamline field request checks. - Updated DockerResolver to utilize the new helper for determining if the sizeRootFs field is requested. - Added unit tests to ensure correct behavior of field request handling in various scenarios. - Improved clarity and maintainability of the resolver code.
1 parent f32ed7f commit a3168b5

File tree

5 files changed

+431
-100
lines changed

5 files changed

+431
-100
lines changed

api/generated-schema.graphql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1093,7 +1093,7 @@ type DockerContainer implements Node {
10931093
created: Int!
10941094
ports: [ContainerPort!]!
10951095

1096-
"""Total size of all the files in the container in bytes"""
1096+
"""Total size of all files in the container (in bytes)"""
10971097
sizeRootFs: BigInt
10981098
labels: JSON
10991099
state: ContainerState!

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

Lines changed: 33 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ import { ContainerState, DockerContainer } from '@app/unraid-api/graph/resolvers
88
import { DockerResolver } from '@app/unraid-api/graph/resolvers/docker/docker.resolver.js';
99
import { DockerService } from '@app/unraid-api/graph/resolvers/docker/docker.service.js';
1010
import { DockerOrganizerService } from '@app/unraid-api/graph/resolvers/docker/organizer/docker-organizer.service.js';
11+
import { GraphQLFieldHelper } from '@app/unraid-api/utils/graphql-field-helper.js';
12+
13+
vi.mock('@app/unraid-api/utils/graphql-field-helper.js', () => ({
14+
GraphQLFieldHelper: {
15+
isFieldRequested: vi.fn(),
16+
},
17+
}));
1118

1219
describe('DockerResolver', () => {
1320
let resolver: DockerResolver;
@@ -41,6 +48,9 @@ describe('DockerResolver', () => {
4148

4249
resolver = module.get<DockerResolver>(DockerResolver);
4350
dockerService = module.get<DockerService>(DockerService);
51+
52+
// Reset mocks before each test
53+
vi.clearAllMocks();
4454
});
4555

4656
it('should be defined', () => {
@@ -80,19 +90,13 @@ describe('DockerResolver', () => {
8090
},
8191
];
8292
vi.mocked(dockerService.getContainers).mockResolvedValue(mockContainers);
93+
vi.mocked(GraphQLFieldHelper.isFieldRequested).mockReturnValue(false);
8394

84-
const mockInfo = {
85-
fieldNodes: [
86-
{
87-
selectionSet: {
88-
selections: [],
89-
},
90-
},
91-
],
92-
} as any;
95+
const mockInfo = {} as any;
9396

9497
const result = await resolver.containers(false, mockInfo);
9598
expect(result).toEqual(mockContainers);
99+
expect(GraphQLFieldHelper.isFieldRequested).toHaveBeenCalledWith(mockInfo, 'sizeRootFs');
96100
expect(dockerService.getContainers).toHaveBeenCalledWith({ skipCache: false, size: false });
97101
});
98102

@@ -113,99 +117,48 @@ describe('DockerResolver', () => {
113117
},
114118
];
115119
vi.mocked(dockerService.getContainers).mockResolvedValue(mockContainers);
120+
vi.mocked(GraphQLFieldHelper.isFieldRequested).mockReturnValue(true);
116121

117-
const mockInfoWithSize = {
118-
fieldNodes: [
119-
{
120-
selectionSet: {
121-
selections: [
122-
{
123-
kind: 'Field',
124-
name: { value: 'sizeRootFs' },
125-
},
126-
],
127-
},
128-
},
129-
],
130-
} as any;
122+
const mockInfo = {} as any;
131123

132-
const result = await resolver.containers(false, mockInfoWithSize);
124+
const result = await resolver.containers(false, mockInfo);
133125
expect(result).toEqual(mockContainers);
126+
expect(GraphQLFieldHelper.isFieldRequested).toHaveBeenCalledWith(mockInfo, 'sizeRootFs');
134127
expect(dockerService.getContainers).toHaveBeenCalledWith({ skipCache: false, size: true });
135128
});
136129

137-
it('should request size when inline fragment is present', async () => {
130+
it('should request size when GraphQLFieldHelper indicates sizeRootFs is requested', async () => {
138131
const mockContainers: DockerContainer[] = [];
139132
vi.mocked(dockerService.getContainers).mockResolvedValue(mockContainers);
133+
vi.mocked(GraphQLFieldHelper.isFieldRequested).mockReturnValue(true);
140134

141-
const mockInfoWithFragment = {
142-
fieldNodes: [
143-
{
144-
selectionSet: {
145-
selections: [
146-
{
147-
kind: 'InlineFragment',
148-
},
149-
],
150-
},
151-
},
152-
],
153-
} as any;
135+
const mockInfo = {} as any;
154136

155-
await resolver.containers(false, mockInfoWithFragment);
137+
await resolver.containers(false, mockInfo);
138+
expect(GraphQLFieldHelper.isFieldRequested).toHaveBeenCalledWith(mockInfo, 'sizeRootFs');
156139
expect(dockerService.getContainers).toHaveBeenCalledWith({ skipCache: false, size: true });
157140
});
158141

159-
it('should request size when fragment spread is present', async () => {
142+
it('should not request size when GraphQLFieldHelper indicates sizeRootFs is not requested', async () => {
160143
const mockContainers: DockerContainer[] = [];
161144
vi.mocked(dockerService.getContainers).mockResolvedValue(mockContainers);
145+
vi.mocked(GraphQLFieldHelper.isFieldRequested).mockReturnValue(false);
162146

163-
const mockInfoWithFragmentSpread = {
164-
fieldNodes: [
165-
{
166-
selectionSet: {
167-
selections: [
168-
{
169-
kind: 'FragmentSpread',
170-
},
171-
],
172-
},
173-
},
174-
],
175-
} as any;
147+
const mockInfo = {} as any;
176148

177-
await resolver.containers(false, mockInfoWithFragmentSpread);
178-
expect(dockerService.getContainers).toHaveBeenCalledWith({ skipCache: false, size: true });
149+
await resolver.containers(false, mockInfo);
150+
expect(GraphQLFieldHelper.isFieldRequested).toHaveBeenCalledWith(mockInfo, 'sizeRootFs');
151+
expect(dockerService.getContainers).toHaveBeenCalledWith({ skipCache: false, size: false });
179152
});
180153

181-
it('should not request size when other fields are requested', async () => {
154+
it('should handle skipCache parameter', async () => {
182155
const mockContainers: DockerContainer[] = [];
183156
vi.mocked(dockerService.getContainers).mockResolvedValue(mockContainers);
157+
vi.mocked(GraphQLFieldHelper.isFieldRequested).mockReturnValue(false);
184158

185-
const mockInfoWithOtherFields = {
186-
fieldNodes: [
187-
{
188-
selectionSet: {
189-
selections: [
190-
{
191-
kind: 'Field',
192-
name: { value: 'id' },
193-
},
194-
{
195-
kind: 'Field',
196-
name: { value: 'names' },
197-
},
198-
{
199-
kind: 'Field',
200-
name: { value: 'state' },
201-
},
202-
],
203-
},
204-
},
205-
],
206-
} as any;
159+
const mockInfo = {} as any;
207160

208-
await resolver.containers(false, mockInfoWithOtherFields);
209-
expect(dockerService.getContainers).toHaveBeenCalledWith({ skipCache: false, size: false });
161+
await resolver.containers(true, mockInfo);
162+
expect(dockerService.getContainers).toHaveBeenCalledWith({ skipCache: true, size: false });
210163
});
211164
});

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

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { DockerService } from '@app/unraid-api/graph/resolvers/docker/docker.ser
1616
import { DockerOrganizerService } from '@app/unraid-api/graph/resolvers/docker/organizer/docker-organizer.service.js';
1717
import { DEFAULT_ORGANIZER_ROOT_ID } from '@app/unraid-api/organizer/organizer.js';
1818
import { ResolvedOrganizerV1 } from '@app/unraid-api/organizer/organizer.model.js';
19+
import { GraphQLFieldHelper } from '@app/unraid-api/utils/graphql-field-helper.js';
1920

2021
@Resolver(() => Docker)
2122
export class DockerResolver {
@@ -45,8 +46,7 @@ export class DockerResolver {
4546
@Args('skipCache', { defaultValue: false, type: () => Boolean }) skipCache: boolean,
4647
@Info() info: GraphQLResolveInfo
4748
) {
48-
// Check if sizeRootFs field is requested in the query
49-
const requestsSize = this.isFieldRequested(info, 'sizeRootFs');
49+
const requestsSize = GraphQLFieldHelper.isFieldRequested(info, 'sizeRootFs');
5050
return this.dockerService.getContainers({ skipCache, size: requestsSize });
5151
}
5252

@@ -146,21 +146,4 @@ export class DockerResolver {
146146
public async containerUpdateStatuses() {
147147
return this.dockerPhpService.getContainerUpdateStatuses();
148148
}
149-
150-
private isFieldRequested(info: GraphQLResolveInfo, fieldName: string): boolean {
151-
const selections = info.fieldNodes[0]?.selectionSet?.selections;
152-
if (!selections) return false;
153-
154-
for (const selection of selections) {
155-
if (selection.kind === 'Field' && selection.name.value === fieldName) {
156-
return true;
157-
}
158-
// Check nested selections for fragments
159-
if (selection.kind === 'InlineFragment' || selection.kind === 'FragmentSpread') {
160-
// For simplicity, if we see fragments, assume the field might be requested
161-
return true;
162-
}
163-
}
164-
return false;
165-
}
166149
}

0 commit comments

Comments
 (0)