Skip to content

Commit f004e94

Browse files
1.0.14
1 parent 3b49d56 commit f004e94

6 files changed

Lines changed: 135 additions & 3 deletions

File tree

dist/index.d.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,49 @@
11
import { NextFunction, Request, Response } from 'express';
22
export declare class GraphQLQueryPurifier {
3+
/**
4+
* The path to the directory containing GraphQL (.gql) files.
5+
* @private
6+
*/
37
private gqlPath;
8+
/**
9+
* A map of allowed query names to their corresponding GraphQL query strings.
10+
* @private
11+
*/
412
private queryMap;
13+
/**
14+
* Flag to allow Apollo Studio's introspection queries.
15+
* @private
16+
*/
517
private allowStudio?;
18+
/**
19+
* Flag to indicate whether to allow all queries (bypassing purifier).
20+
* @private
21+
*/
622
private allowAll;
23+
/**
24+
* Constructs a GraphQLQueryPurifier instance.
25+
* @param {Object} params - Configuration parameters.
26+
* @param {string} params.gqlPath - Path to the directory containing .gql files.
27+
* @param {boolean} [params.allowAll=false] - Whether to allow all queries.
28+
* @param {boolean} [params.allowStudio=false] - Whether to allow Apollo Studio introspection queries.
29+
*/
730
constructor({ gqlPath, allowAll, allowStudio, }: {
831
gqlPath: string;
932
allowStudio?: boolean;
1033
allowAll?: boolean;
1134
});
35+
startWatchingFiles(): void;
36+
/**
37+
* Loads queries from .gql files in the specified directory and updates the query map.
38+
* @private
39+
*/
1240
private loadQueries;
41+
/**
42+
* Middleware function to filter incoming GraphQL queries based on the allowed list.
43+
* If a query is not allowed, it's replaced with a minimal query.
44+
* @param {Request} req - The request object.
45+
* @param {Response} res - The response object.
46+
* @param {NextFunction} next - The next middleware function in the stack.
47+
*/
1348
filter: (req: Request, res: Response, next: NextFunction) => void | Response<any, Record<string, any>>;
1449
}

dist/index.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,21 @@ const path_1 = __importDefault(require("path"));
1111
const glob_1 = __importDefault(require("glob"));
1212
const merge_1 = require("./merge");
1313
class GraphQLQueryPurifier {
14+
/**
15+
* Constructs a GraphQLQueryPurifier instance.
16+
* @param {Object} params - Configuration parameters.
17+
* @param {string} params.gqlPath - Path to the directory containing .gql files.
18+
* @param {boolean} [params.allowAll=false] - Whether to allow all queries.
19+
* @param {boolean} [params.allowStudio=false] - Whether to allow Apollo Studio introspection queries.
20+
*/
1421
constructor({ gqlPath, allowAll = false, allowStudio = false, }) {
22+
/**
23+
* Middleware function to filter incoming GraphQL queries based on the allowed list.
24+
* If a query is not allowed, it's replaced with a minimal query.
25+
* @param {Request} req - The request object.
26+
* @param {Response} res - The response object.
27+
* @param {NextFunction} next - The next middleware function in the stack.
28+
*/
1529
this.filter = (req, res, next) => {
1630
if (this.allowAll)
1731
return next();
@@ -50,9 +64,22 @@ class GraphQLQueryPurifier {
5064
this.gqlPath = gqlPath;
5165
this.queryMap = {};
5266
this.loadQueries();
67+
this.startWatchingFiles();
5368
this.allowAll = allowAll;
5469
this.allowStudio = allowStudio;
5570
}
71+
startWatchingFiles() {
72+
fs_1.default.watch(this.gqlPath, { recursive: true }, (eventType, filename) => {
73+
if (filename && path_1.default.extname(filename) === '.gql') {
74+
console.log(`Detected ${eventType} in ${filename}`);
75+
this.loadQueries();
76+
}
77+
});
78+
}
79+
/**
80+
* Loads queries from .gql files in the specified directory and updates the query map.
81+
* @private
82+
*/
5683
loadQueries() {
5784
const files = glob_1.default.sync(`${this.gqlPath}/**/*.gql`.replace(/\\/g, '/'));
5885
files.forEach((file) => {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "graphql-query-purifier",
3-
"version": "1.0.12",
3+
"version": "1.0.13",
44
"description": "A small library to match .gql queries vs user input. Removes fields from user requests that are not expected by your frontend code.",
55
"main": "./dist/index.js",
66
"author": "multipliedtwice",

src/__tests__/merge.spec.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,23 @@ describe('mergeQueries', () => {
201201
salaries {
202202
amount
203203
}
204+
}`,
205+
];
206+
const expected = ``;
207+
expect(mergeQueries(requestQuery, allowedQueries)).toBe(expected);
208+
});
209+
210+
test('should handle subqueries', () => {
211+
const requestQuery = `query GetDepartment {
212+
departments {
213+
name
214+
}
215+
}`;
216+
const allowedQueries = [
217+
`query departments {
218+
departments {
219+
id
220+
}
204221
}`,
205222
];
206223
const expected = ``;

src/index.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,38 @@ import glob from 'glob';
77
import { mergeQueries } from './merge';
88

99
export class GraphQLQueryPurifier {
10+
/**
11+
* The path to the directory containing GraphQL (.gql) files.
12+
* @private
13+
*/
14+
1015
private gqlPath: string;
16+
/**
17+
* A map of allowed query names to their corresponding GraphQL query strings.
18+
* @private
19+
*/
20+
1121
private queryMap: { [key: string]: string };
22+
/**
23+
* Flag to allow Apollo Studio's introspection queries.
24+
* @private
25+
*/
26+
1227
private allowStudio?: boolean;
28+
/**
29+
* Flag to indicate whether to allow all queries (bypassing purifier).
30+
* @private
31+
*/
32+
1333
private allowAll: boolean;
1434

35+
/**
36+
* Constructs a GraphQLQueryPurifier instance.
37+
* @param {Object} params - Configuration parameters.
38+
* @param {string} params.gqlPath - Path to the directory containing .gql files.
39+
* @param {boolean} [params.allowAll=false] - Whether to allow all queries.
40+
* @param {boolean} [params.allowStudio=false] - Whether to allow Apollo Studio introspection queries.
41+
*/
1542
constructor({
1643
gqlPath,
1744
allowAll = false,
@@ -24,10 +51,24 @@ export class GraphQLQueryPurifier {
2451
this.gqlPath = gqlPath;
2552
this.queryMap = {};
2653
this.loadQueries();
54+
this.startWatchingFiles();
2755
this.allowAll = allowAll;
2856
this.allowStudio = allowStudio;
2957
}
3058

59+
public startWatchingFiles() {
60+
fs.watch(this.gqlPath, { recursive: true }, (eventType, filename) => {
61+
if (filename && path.extname(filename) === '.gql') {
62+
console.log(`Detected ${eventType} in ${filename}`);
63+
this.loadQueries();
64+
}
65+
});
66+
}
67+
68+
/**
69+
* Loads queries from .gql files in the specified directory and updates the query map.
70+
* @private
71+
*/
3172
private loadQueries() {
3273
const files = glob.sync(`${this.gqlPath}/**/*.gql`.replace(/\\/g, '/'));
3374

@@ -74,6 +115,13 @@ export class GraphQLQueryPurifier {
74115
});
75116
}
76117

118+
/**
119+
* Middleware function to filter incoming GraphQL queries based on the allowed list.
120+
* If a query is not allowed, it's replaced with a minimal query.
121+
* @param {Request} req - The request object.
122+
* @param {Response} res - The response object.
123+
* @param {NextFunction} next - The next middleware function in the stack.
124+
*/
77125
public filter = (req: Request, res: Response, next: NextFunction) => {
78126
if (this.allowAll) return next();
79127
if (
@@ -103,7 +151,11 @@ export class GraphQLQueryPurifier {
103151

104152
if (req.body && req.body.query) {
105153
// Use mergeQueries to filter the incoming request query
106-
const filteredQuery = mergeQueries(req.body.query, allowedQueries);
154+
const filteredQuery = mergeQueries(
155+
req.body.query,
156+
allowedQueries,
157+
req.body.operationName
158+
);
107159

108160
if (!filteredQuery.trim()) {
109161
console.warn(

src/merge.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ function getPath(node: FieldNode, ancestors: ASTNode[]) {
1111

1212
export function mergeQueries(
1313
requestQuery: string,
14-
allowedQueries: string[]
14+
allowedQueries: string[],
15+
operationName?: string
1516
): string {
1617
if (!requestQuery.trim()) {
1718
return '';

0 commit comments

Comments
 (0)