Skip to content

Commit 43adee9

Browse files
authored
Merge pull request #3 from mayademcom/loading-from-gql-files
feat: added withQuery and withVariables methods.
2 parents f339a94 + cf71c26 commit 43adee9

11 files changed

+936
-239
lines changed

package.json

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@
1717
"test": "jest",
1818
"test:watch": "jest --watch",
1919
"test:cov": "jest --coverage",
20-
"test:debug": "node --inspect-brk --require ts-node/register --require tsconfig-paths/register ./node_modules/jest/bin/jest.js --runInBand --no-cache",
21-
"test:e2e": "jest --config ./test/jest-e2e.json"
20+
"test:debug": "node --inspect-brk --require ts-node/register --require tsconfig-paths/register ./node_modules/jest/bin/jest.js --runInBand --no-cache"
2221
},
2322
"peerDependencies": {
2423
"@nestjs/common": "^11.x"
2524
},
25+
"dependencies": {
26+
"graphql-request": "^7.4.0"
27+
},
2628
"devDependencies": {
2729
"@eslint/eslintrc": "^3.2.0",
2830
"@eslint/js": "^9.18.0",
@@ -56,7 +58,7 @@
5658
"json",
5759
"ts"
5860
],
59-
"rootDir": "src",
61+
"rootDir": ".",
6062
"testRegex": ".*\\.spec\\.ts$",
6163
"transform": {
6264
"^.+\\.(t|j)s$": "ts-jest"
@@ -66,10 +68,11 @@
6668
"!**/*.spec.ts",
6769
"!**/index.ts"
6870
],
69-
"coverageDirectory": "../coverage",
70-
"testEnvironment": "node"
71-
},
72-
"dependencies": {
73-
"graphql-request": "^7.4.0"
71+
"coverageDirectory": "coverage",
72+
"testEnvironment": "node",
73+
"roots": [
74+
"<rootDir>/src/",
75+
"<rootDir>/test/"
76+
]
7477
}
75-
}
78+
}

sonar-project.properties

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@ sonar.organization=mayademcom
33
sonar.sources=src
44
sonar.exclusions=**/node_modules/**,**/dist/**,**/*.spec.ts
55
sonar.javascript.node.maxspace=4096
6-
sonar.javascript.lcov.reportPaths=./coverage/lcov.info
7-
6+
sonar.javascript.lcov.reportPaths=./coverage/lcov.info
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import * as fs from 'fs';
2+
import * as path from 'path';
3+
4+
import { Test, TestingModule } from '@nestjs/testing';
5+
6+
import { GraphQLLoaderService } from './graphql-loader.service';
7+
8+
jest.mock('fs');
9+
jest.mock('path');
10+
11+
describe('GraphQLLoaderService', () => {
12+
let service: GraphQLLoaderService;
13+
14+
const mockFilePath = 'queries/test.graphql';
15+
const mockFullPath = '/app/queries/test.graphql';
16+
const mockQuery = 'query Test { test }';
17+
const mockedReadFileSync = fs.readFileSync as jest.MockedFunction<
18+
typeof fs.readFileSync
19+
>;
20+
21+
beforeEach(async () => {
22+
jest.clearAllMocks();
23+
24+
(path.join as jest.Mock).mockReturnValue(mockFullPath);
25+
26+
const module: TestingModule = await Test.createTestingModule({
27+
providers: [GraphQLLoaderService],
28+
}).compile();
29+
30+
service = module.get<GraphQLLoaderService>(GraphQLLoaderService);
31+
});
32+
33+
it('should be defined', () => {
34+
expect(service).toBeDefined();
35+
});
36+
37+
it('should load query from file when cache is empty', () => {
38+
mockedReadFileSync.mockReturnValue(mockQuery as string);
39+
40+
const result = service.loadQuery(mockFilePath);
41+
42+
expect(result).toBe(mockQuery);
43+
expect(fs.readFileSync).toHaveBeenCalledWith(mockFullPath, 'utf-8');
44+
});
45+
46+
it('should cache the query after loading from file', () => {
47+
mockedReadFileSync.mockReturnValue(mockQuery as string);
48+
49+
service.loadQuery(mockFilePath);
50+
mockedReadFileSync.mockClear();
51+
52+
const cachedResult = service.loadQuery(mockFilePath);
53+
54+
expect(cachedResult).toBe(mockQuery);
55+
expect(mockedReadFileSync).not.toHaveBeenCalled();
56+
});
57+
58+
it('should throw error if file read fails', () => {
59+
(fs.readFileSync as jest.Mock).mockImplementation(() => {
60+
throw new Error('File not found');
61+
});
62+
63+
expect(() => service.loadQuery(mockFilePath)).toThrow(
64+
`Failed to load GraphQL query from ${mockFilePath}: File not found`,
65+
);
66+
});
67+
68+
it('should clear cache', () => {
69+
(fs.readFileSync as jest.Mock).mockReturnValue(mockQuery);
70+
71+
service.loadQuery(mockFilePath);
72+
service.clearCache();
73+
74+
service.loadQuery(mockFilePath);
75+
76+
expect(fs.readFileSync).toHaveBeenCalledTimes(2);
77+
});
78+
});
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import * as fs from 'node:fs';
2+
import * as path from 'node:path';
3+
4+
import { Injectable } from '@nestjs/common';
5+
6+
@Injectable()
7+
export class GraphQLLoaderService {
8+
private readonly queryCache = new Map<string, string>();
9+
10+
loadQuery(filePath: string): string {
11+
let query = this.loadQueryFromCache(filePath);
12+
if (!query) query = this.loadQueryFromFile(filePath);
13+
this.saveQueryToCache(filePath, query);
14+
return query;
15+
}
16+
17+
private loadQueryFromCache(filePath: string) {
18+
let query: string | null = null;
19+
if (this.queryCache.has(filePath)) {
20+
query = this.queryCache.get(filePath)!;
21+
}
22+
return query;
23+
}
24+
25+
private loadQueryFromFile(filePath: string) {
26+
try {
27+
const fullPath = path.join(process.cwd(), filePath);
28+
const query = fs.readFileSync(fullPath, 'utf-8');
29+
return query;
30+
} catch (error) {
31+
throw new Error(
32+
`Failed to load GraphQL query from ${filePath}: ${(error as Error).message}`,
33+
);
34+
}
35+
}
36+
37+
private saveQueryToCache(filePath: string, query: string) {
38+
this.queryCache.set(filePath, query);
39+
}
40+
41+
clearCache(): void {
42+
this.queryCache.clear();
43+
}
44+
}

src/hasura.module.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { DynamicModule, Module } from '@nestjs/common';
22
import { HasuraAsyncConfig, HasuraConfig } from './models';
33

4+
import { GraphQLLoaderService } from './graphql-loader/graphql-loader.service';
45
import { HasuraService } from './hasura.service';
56

67
@Module({})
@@ -27,6 +28,7 @@ export class HasuraModule {
2728
module: HasuraModule,
2829
global: false,
2930
providers: [
31+
GraphQLLoaderService,
3032
HasuraService,
3133
{
3234
provide: 'HASURA_CONFIG',
@@ -64,6 +66,7 @@ export class HasuraModule {
6466
global: false,
6567
imports: config.imports,
6668
providers: [
69+
GraphQLLoaderService,
6770
HasuraService,
6871
{
6972
provide: 'HASURA_CONFIG',

0 commit comments

Comments
 (0)