@envelop/resource-limitations 2.4.0-alpha-e9434aa.0 → 2.4.0-alpha-05dbec7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/index.js +24 -29
- package/package.json +3 -3
package/esm/index.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const extended_validation_1 = require("@envelop/extended-validation");
|
|
6
|
-
const graphql_1 = require("graphql");
|
|
7
|
-
const utils_1 = require("@graphql-tools/utils");
|
|
1
|
+
import { handleStreamOrSingleExecutionResult } from '@envelop/core';
|
|
2
|
+
import { useExtendedValidation } from '@envelop/extended-validation';
|
|
3
|
+
import { GraphQLError, GraphQLInt, GraphQLList, GraphQLNonNull, GraphQLObjectType, isScalarType, } from 'graphql';
|
|
4
|
+
import { getArgumentValues } from '@graphql-tools/utils';
|
|
8
5
|
const getWrappedType = (graphqlType) => {
|
|
9
|
-
if (graphqlType instanceof
|
|
6
|
+
if (graphqlType instanceof GraphQLList || graphqlType instanceof GraphQLNonNull) {
|
|
10
7
|
return getWrappedType(graphqlType.ofType);
|
|
11
8
|
}
|
|
12
9
|
return graphqlType;
|
|
13
10
|
};
|
|
14
|
-
const isValidArgType = (type, paginationArgumentTypes) => type ===
|
|
11
|
+
const isValidArgType = (type, paginationArgumentTypes) => type === GraphQLInt || (isScalarType(type) && !!paginationArgumentTypes && paginationArgumentTypes.includes(type.name));
|
|
15
12
|
const hasFieldDefConnectionArgs = (field, argumentTypes) => {
|
|
16
13
|
let hasFirst = false;
|
|
17
14
|
let hasLast = false;
|
|
@@ -34,13 +31,13 @@ const buildMissingPaginationFieldErrorMessage = (params) => `Missing pagination
|
|
|
34
31
|
' field argument.';
|
|
35
32
|
const buildInvalidPaginationRangeErrorMessage = (params) => `Invalid pagination argument for field ${params.fieldName}. ` +
|
|
36
33
|
`The value for the '${params.argumentName}' argument must be an integer within ${params.paginationArgumentMinimum}-${params.paginationArgumentMaximum}.`;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
export const defaultNodeCostLimit = 500000;
|
|
35
|
+
export const defaultPaginationArgumentMaximum = 100;
|
|
36
|
+
export const defaultPaginationArgumentMinimum = 1;
|
|
40
37
|
/**
|
|
41
38
|
* Validate whether a user is allowed to execute a certain GraphQL operation.
|
|
42
39
|
*/
|
|
43
|
-
const ResourceLimitationValidationRule = (params) => (context, executionArgs) => {
|
|
40
|
+
export const ResourceLimitationValidationRule = (params) => (context, executionArgs) => {
|
|
44
41
|
const { paginationArgumentMaximum, paginationArgumentMinimum } = params;
|
|
45
42
|
const nodeCostStack = [];
|
|
46
43
|
let totalNodeCost = 0;
|
|
@@ -51,9 +48,9 @@ const ResourceLimitationValidationRule = (params) => (context, executionArgs) =>
|
|
|
51
48
|
const fieldDef = context.getFieldDef();
|
|
52
49
|
// if it is not found the query is invalid and graphql validation will complain
|
|
53
50
|
if (fieldDef != null) {
|
|
54
|
-
const argumentValues =
|
|
51
|
+
const argumentValues = getArgumentValues(fieldDef, fieldNode, executionArgs.variableValues || undefined);
|
|
55
52
|
const type = getWrappedType(fieldDef.type);
|
|
56
|
-
if (type instanceof
|
|
53
|
+
if (type instanceof GraphQLObjectType && type.name.endsWith('Connection')) {
|
|
57
54
|
let nodeCost = 1;
|
|
58
55
|
connectionFieldMap.add(fieldNode);
|
|
59
56
|
const { hasFirst, hasLast } = hasFieldDefConnectionArgs(fieldDef, params.paginationArgumentTypes);
|
|
@@ -63,7 +60,7 @@ const ResourceLimitationValidationRule = (params) => (context, executionArgs) =>
|
|
|
63
60
|
}
|
|
64
61
|
else if (hasFirst === true || hasLast === true) {
|
|
65
62
|
if ('first' in argumentValues === false && 'last' in argumentValues === false) {
|
|
66
|
-
context.reportError(new
|
|
63
|
+
context.reportError(new GraphQLError(buildMissingPaginationFieldErrorMessage({
|
|
67
64
|
fieldName: fieldDef.name,
|
|
68
65
|
hasFirst,
|
|
69
66
|
hasLast,
|
|
@@ -71,7 +68,7 @@ const ResourceLimitationValidationRule = (params) => (context, executionArgs) =>
|
|
|
71
68
|
}
|
|
72
69
|
else if ('first' in argumentValues === true && 'last' in argumentValues === false) {
|
|
73
70
|
if (argumentValues.first < paginationArgumentMinimum || argumentValues.first > paginationArgumentMaximum) {
|
|
74
|
-
context.reportError(new
|
|
71
|
+
context.reportError(new GraphQLError(buildInvalidPaginationRangeErrorMessage({
|
|
75
72
|
paginationArgumentMaximum,
|
|
76
73
|
paginationArgumentMinimum,
|
|
77
74
|
argumentName: 'first',
|
|
@@ -85,7 +82,7 @@ const ResourceLimitationValidationRule = (params) => (context, executionArgs) =>
|
|
|
85
82
|
}
|
|
86
83
|
else if ('last' in argumentValues === true && 'false' in argumentValues === false) {
|
|
87
84
|
if (argumentValues.last < paginationArgumentMinimum || argumentValues.last > paginationArgumentMaximum) {
|
|
88
|
-
context.reportError(new
|
|
85
|
+
context.reportError(new GraphQLError(buildInvalidPaginationRangeErrorMessage({
|
|
89
86
|
paginationArgumentMaximum,
|
|
90
87
|
paginationArgumentMinimum,
|
|
91
88
|
argumentName: 'last',
|
|
@@ -98,7 +95,7 @@ const ResourceLimitationValidationRule = (params) => (context, executionArgs) =>
|
|
|
98
95
|
}
|
|
99
96
|
}
|
|
100
97
|
else {
|
|
101
|
-
context.reportError(new
|
|
98
|
+
context.reportError(new GraphQLError(buildMissingPaginationFieldErrorMessage({
|
|
102
99
|
fieldName: fieldDef.name,
|
|
103
100
|
hasFirst,
|
|
104
101
|
hasLast,
|
|
@@ -123,19 +120,18 @@ const ResourceLimitationValidationRule = (params) => (context, executionArgs) =>
|
|
|
123
120
|
totalNodeCost = 1;
|
|
124
121
|
}
|
|
125
122
|
if (totalNodeCost > params.nodeCostLimit) {
|
|
126
|
-
context.reportError(new
|
|
123
|
+
context.reportError(new GraphQLError(`Cannot request more than ${params.nodeCostLimit} nodes in a single document. Please split your operation into multiple sub operations or reduce the amount of requested nodes.`, documentNode));
|
|
127
124
|
}
|
|
128
125
|
(_a = params.reportNodeCost) === null || _a === void 0 ? void 0 : _a.call(params, totalNodeCost, executionArgs);
|
|
129
126
|
},
|
|
130
127
|
},
|
|
131
128
|
};
|
|
132
129
|
};
|
|
133
|
-
|
|
134
|
-
const useResourceLimitations = (params) => {
|
|
130
|
+
export const useResourceLimitations = (params) => {
|
|
135
131
|
var _a, _b, _c, _d;
|
|
136
|
-
const paginationArgumentMaximum = (_a = params === null || params === void 0 ? void 0 : params.paginationArgumentMaximum) !== null && _a !== void 0 ? _a :
|
|
137
|
-
const paginationArgumentMinimum = (_b = params === null || params === void 0 ? void 0 : params.paginationArgumentMinimum) !== null && _b !== void 0 ? _b :
|
|
138
|
-
const nodeCostLimit = (_c = params === null || params === void 0 ? void 0 : params.nodeCostLimit) !== null && _c !== void 0 ? _c :
|
|
132
|
+
const paginationArgumentMaximum = (_a = params === null || params === void 0 ? void 0 : params.paginationArgumentMaximum) !== null && _a !== void 0 ? _a : defaultPaginationArgumentMaximum;
|
|
133
|
+
const paginationArgumentMinimum = (_b = params === null || params === void 0 ? void 0 : params.paginationArgumentMinimum) !== null && _b !== void 0 ? _b : defaultPaginationArgumentMinimum;
|
|
134
|
+
const nodeCostLimit = (_c = params === null || params === void 0 ? void 0 : params.nodeCostLimit) !== null && _c !== void 0 ? _c : defaultNodeCostLimit;
|
|
139
135
|
const extensions = (_d = params === null || params === void 0 ? void 0 : params.extensions) !== null && _d !== void 0 ? _d : false;
|
|
140
136
|
const nodeCostMap = new WeakMap();
|
|
141
137
|
const handleResult = ({ result, args }) => {
|
|
@@ -151,9 +147,9 @@ const useResourceLimitations = (params) => {
|
|
|
151
147
|
};
|
|
152
148
|
return {
|
|
153
149
|
onPluginInit({ addPlugin }) {
|
|
154
|
-
addPlugin(
|
|
150
|
+
addPlugin(useExtendedValidation({
|
|
155
151
|
rules: [
|
|
156
|
-
|
|
152
|
+
ResourceLimitationValidationRule({
|
|
157
153
|
nodeCostLimit,
|
|
158
154
|
paginationArgumentMaximum,
|
|
159
155
|
paginationArgumentMinimum,
|
|
@@ -171,10 +167,9 @@ const useResourceLimitations = (params) => {
|
|
|
171
167
|
onExecute({ args }) {
|
|
172
168
|
return {
|
|
173
169
|
onExecuteDone(payload) {
|
|
174
|
-
return
|
|
170
|
+
return handleStreamOrSingleExecutionResult(payload, ({ result }) => handleResult({ result, args }));
|
|
175
171
|
},
|
|
176
172
|
};
|
|
177
173
|
},
|
|
178
174
|
};
|
|
179
175
|
};
|
|
180
|
-
exports.useResourceLimitations = useResourceLimitations;
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@envelop/resource-limitations",
|
|
3
|
-
"version": "2.4.0-alpha-
|
|
3
|
+
"version": "2.4.0-alpha-05dbec7.0",
|
|
4
4
|
"description": "A rate-limit implementation based on resource limitations and static calculation of the score (similar to GitHub GraphQL API)",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"peerDependencies": {
|
|
7
|
-
"@envelop/core": "2.4.0-alpha-
|
|
7
|
+
"@envelop/core": "2.4.0-alpha-05dbec7.0",
|
|
8
8
|
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@envelop/extended-validation": "1.7.0-alpha-
|
|
11
|
+
"@envelop/extended-validation": "1.7.0-alpha-05dbec7.0",
|
|
12
12
|
"@graphql-tools/utils": "^8.6.1"
|
|
13
13
|
},
|
|
14
14
|
"repository": {
|