@envelop/resource-limitations 2.4.0-alpha-aca44e1.0 → 2.4.0-alpha-385b86b.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/{index.js → cjs/index.js} +26 -32
- package/cjs/package.json +1 -0
- package/{index.mjs → esm/index.js} +6 -9
- package/package.json +34 -13
- package/{index.d.ts → typings/index.d.ts} +1 -1
- package/README.md +0 -29
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const utils = require('@graphql-tools/utils');
|
|
9
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useResourceLimitations = exports.ResourceLimitationValidationRule = exports.defaultPaginationArgumentMinimum = exports.defaultPaginationArgumentMaximum = exports.defaultNodeCostLimit = void 0;
|
|
4
|
+
const core_1 = require("@envelop/core");
|
|
5
|
+
const extended_validation_1 = require("@envelop/extended-validation");
|
|
6
|
+
const graphql_1 = require("graphql");
|
|
7
|
+
const utils_1 = require("@graphql-tools/utils");
|
|
10
8
|
const getWrappedType = (graphqlType) => {
|
|
11
|
-
if (graphqlType instanceof
|
|
9
|
+
if (graphqlType instanceof graphql_1.GraphQLList || graphqlType instanceof graphql_1.GraphQLNonNull) {
|
|
12
10
|
return getWrappedType(graphqlType.ofType);
|
|
13
11
|
}
|
|
14
12
|
return graphqlType;
|
|
15
13
|
};
|
|
16
|
-
const isValidArgType = (type, paginationArgumentTypes) => type ===
|
|
14
|
+
const isValidArgType = (type, paginationArgumentTypes) => type === graphql_1.GraphQLInt || ((0, graphql_1.isScalarType)(type) && !!paginationArgumentTypes && paginationArgumentTypes.includes(type.name));
|
|
17
15
|
const hasFieldDefConnectionArgs = (field, argumentTypes) => {
|
|
18
16
|
let hasFirst = false;
|
|
19
17
|
let hasLast = false;
|
|
@@ -36,9 +34,9 @@ const buildMissingPaginationFieldErrorMessage = (params) => `Missing pagination
|
|
|
36
34
|
' field argument.';
|
|
37
35
|
const buildInvalidPaginationRangeErrorMessage = (params) => `Invalid pagination argument for field ${params.fieldName}. ` +
|
|
38
36
|
`The value for the '${params.argumentName}' argument must be an integer within ${params.paginationArgumentMinimum}-${params.paginationArgumentMaximum}.`;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
exports.defaultNodeCostLimit = 500000;
|
|
38
|
+
exports.defaultPaginationArgumentMaximum = 100;
|
|
39
|
+
exports.defaultPaginationArgumentMinimum = 1;
|
|
42
40
|
/**
|
|
43
41
|
* Validate whether a user is allowed to execute a certain GraphQL operation.
|
|
44
42
|
*/
|
|
@@ -53,9 +51,9 @@ const ResourceLimitationValidationRule = (params) => (context, executionArgs) =>
|
|
|
53
51
|
const fieldDef = context.getFieldDef();
|
|
54
52
|
// if it is not found the query is invalid and graphql validation will complain
|
|
55
53
|
if (fieldDef != null) {
|
|
56
|
-
const argumentValues =
|
|
54
|
+
const argumentValues = (0, utils_1.getArgumentValues)(fieldDef, fieldNode, executionArgs.variableValues || undefined);
|
|
57
55
|
const type = getWrappedType(fieldDef.type);
|
|
58
|
-
if (type instanceof
|
|
56
|
+
if (type instanceof graphql_1.GraphQLObjectType && type.name.endsWith('Connection')) {
|
|
59
57
|
let nodeCost = 1;
|
|
60
58
|
connectionFieldMap.add(fieldNode);
|
|
61
59
|
const { hasFirst, hasLast } = hasFieldDefConnectionArgs(fieldDef, params.paginationArgumentTypes);
|
|
@@ -65,7 +63,7 @@ const ResourceLimitationValidationRule = (params) => (context, executionArgs) =>
|
|
|
65
63
|
}
|
|
66
64
|
else if (hasFirst === true || hasLast === true) {
|
|
67
65
|
if ('first' in argumentValues === false && 'last' in argumentValues === false) {
|
|
68
|
-
context.reportError(new
|
|
66
|
+
context.reportError(new graphql_1.GraphQLError(buildMissingPaginationFieldErrorMessage({
|
|
69
67
|
fieldName: fieldDef.name,
|
|
70
68
|
hasFirst,
|
|
71
69
|
hasLast,
|
|
@@ -73,7 +71,7 @@ const ResourceLimitationValidationRule = (params) => (context, executionArgs) =>
|
|
|
73
71
|
}
|
|
74
72
|
else if ('first' in argumentValues === true && 'last' in argumentValues === false) {
|
|
75
73
|
if (argumentValues.first < paginationArgumentMinimum || argumentValues.first > paginationArgumentMaximum) {
|
|
76
|
-
context.reportError(new
|
|
74
|
+
context.reportError(new graphql_1.GraphQLError(buildInvalidPaginationRangeErrorMessage({
|
|
77
75
|
paginationArgumentMaximum,
|
|
78
76
|
paginationArgumentMinimum,
|
|
79
77
|
argumentName: 'first',
|
|
@@ -87,7 +85,7 @@ const ResourceLimitationValidationRule = (params) => (context, executionArgs) =>
|
|
|
87
85
|
}
|
|
88
86
|
else if ('last' in argumentValues === true && 'false' in argumentValues === false) {
|
|
89
87
|
if (argumentValues.last < paginationArgumentMinimum || argumentValues.last > paginationArgumentMaximum) {
|
|
90
|
-
context.reportError(new
|
|
88
|
+
context.reportError(new graphql_1.GraphQLError(buildInvalidPaginationRangeErrorMessage({
|
|
91
89
|
paginationArgumentMaximum,
|
|
92
90
|
paginationArgumentMinimum,
|
|
93
91
|
argumentName: 'last',
|
|
@@ -100,7 +98,7 @@ const ResourceLimitationValidationRule = (params) => (context, executionArgs) =>
|
|
|
100
98
|
}
|
|
101
99
|
}
|
|
102
100
|
else {
|
|
103
|
-
context.reportError(new
|
|
101
|
+
context.reportError(new graphql_1.GraphQLError(buildMissingPaginationFieldErrorMessage({
|
|
104
102
|
fieldName: fieldDef.name,
|
|
105
103
|
hasFirst,
|
|
106
104
|
hasLast,
|
|
@@ -125,18 +123,19 @@ const ResourceLimitationValidationRule = (params) => (context, executionArgs) =>
|
|
|
125
123
|
totalNodeCost = 1;
|
|
126
124
|
}
|
|
127
125
|
if (totalNodeCost > params.nodeCostLimit) {
|
|
128
|
-
context.reportError(new
|
|
126
|
+
context.reportError(new graphql_1.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));
|
|
129
127
|
}
|
|
130
128
|
(_a = params.reportNodeCost) === null || _a === void 0 ? void 0 : _a.call(params, totalNodeCost, executionArgs);
|
|
131
129
|
},
|
|
132
130
|
},
|
|
133
131
|
};
|
|
134
132
|
};
|
|
133
|
+
exports.ResourceLimitationValidationRule = ResourceLimitationValidationRule;
|
|
135
134
|
const useResourceLimitations = (params) => {
|
|
136
135
|
var _a, _b, _c, _d;
|
|
137
|
-
const paginationArgumentMaximum = (_a = params === null || params === void 0 ? void 0 : params.paginationArgumentMaximum) !== null && _a !== void 0 ? _a : defaultPaginationArgumentMaximum;
|
|
138
|
-
const paginationArgumentMinimum = (_b = params === null || params === void 0 ? void 0 : params.paginationArgumentMinimum) !== null && _b !== void 0 ? _b : defaultPaginationArgumentMinimum;
|
|
139
|
-
const nodeCostLimit = (_c = params === null || params === void 0 ? void 0 : params.nodeCostLimit) !== null && _c !== void 0 ? _c : defaultNodeCostLimit;
|
|
136
|
+
const paginationArgumentMaximum = (_a = params === null || params === void 0 ? void 0 : params.paginationArgumentMaximum) !== null && _a !== void 0 ? _a : exports.defaultPaginationArgumentMaximum;
|
|
137
|
+
const paginationArgumentMinimum = (_b = params === null || params === void 0 ? void 0 : params.paginationArgumentMinimum) !== null && _b !== void 0 ? _b : exports.defaultPaginationArgumentMinimum;
|
|
138
|
+
const nodeCostLimit = (_c = params === null || params === void 0 ? void 0 : params.nodeCostLimit) !== null && _c !== void 0 ? _c : exports.defaultNodeCostLimit;
|
|
140
139
|
const extensions = (_d = params === null || params === void 0 ? void 0 : params.extensions) !== null && _d !== void 0 ? _d : false;
|
|
141
140
|
const nodeCostMap = new WeakMap();
|
|
142
141
|
const handleResult = ({ result, args }) => {
|
|
@@ -152,9 +151,9 @@ const useResourceLimitations = (params) => {
|
|
|
152
151
|
};
|
|
153
152
|
return {
|
|
154
153
|
onPluginInit({ addPlugin }) {
|
|
155
|
-
addPlugin(
|
|
154
|
+
addPlugin((0, extended_validation_1.useExtendedValidation)({
|
|
156
155
|
rules: [
|
|
157
|
-
ResourceLimitationValidationRule({
|
|
156
|
+
(0, exports.ResourceLimitationValidationRule)({
|
|
158
157
|
nodeCostLimit,
|
|
159
158
|
paginationArgumentMaximum,
|
|
160
159
|
paginationArgumentMinimum,
|
|
@@ -172,15 +171,10 @@ const useResourceLimitations = (params) => {
|
|
|
172
171
|
onExecute({ args }) {
|
|
173
172
|
return {
|
|
174
173
|
onExecuteDone(payload) {
|
|
175
|
-
return
|
|
174
|
+
return (0, core_1.handleStreamOrSingleExecutionResult)(payload, ({ result }) => handleResult({ result, args }));
|
|
176
175
|
},
|
|
177
176
|
};
|
|
178
177
|
},
|
|
179
178
|
};
|
|
180
179
|
};
|
|
181
|
-
|
|
182
|
-
exports.ResourceLimitationValidationRule = ResourceLimitationValidationRule;
|
|
183
|
-
exports.defaultNodeCostLimit = defaultNodeCostLimit;
|
|
184
|
-
exports.defaultPaginationArgumentMaximum = defaultPaginationArgumentMaximum;
|
|
185
|
-
exports.defaultPaginationArgumentMinimum = defaultPaginationArgumentMinimum;
|
|
186
180
|
exports.useResourceLimitations = useResourceLimitations;
|
package/cjs/package.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { handleStreamOrSingleExecutionResult } from '@envelop/core';
|
|
2
2
|
import { useExtendedValidation } from '@envelop/extended-validation';
|
|
3
|
-
import {
|
|
3
|
+
import { GraphQLError, GraphQLInt, GraphQLList, GraphQLNonNull, GraphQLObjectType, isScalarType, } from 'graphql';
|
|
4
4
|
import { getArgumentValues } from '@graphql-tools/utils';
|
|
5
|
-
|
|
6
5
|
const getWrappedType = (graphqlType) => {
|
|
7
6
|
if (graphqlType instanceof GraphQLList || graphqlType instanceof GraphQLNonNull) {
|
|
8
7
|
return getWrappedType(graphqlType.ofType);
|
|
@@ -32,13 +31,13 @@ const buildMissingPaginationFieldErrorMessage = (params) => `Missing pagination
|
|
|
32
31
|
' field argument.';
|
|
33
32
|
const buildInvalidPaginationRangeErrorMessage = (params) => `Invalid pagination argument for field ${params.fieldName}. ` +
|
|
34
33
|
`The value for the '${params.argumentName}' argument must be an integer within ${params.paginationArgumentMinimum}-${params.paginationArgumentMaximum}.`;
|
|
35
|
-
const defaultNodeCostLimit = 500000;
|
|
36
|
-
const defaultPaginationArgumentMaximum = 100;
|
|
37
|
-
const defaultPaginationArgumentMinimum = 1;
|
|
34
|
+
export const defaultNodeCostLimit = 500000;
|
|
35
|
+
export const defaultPaginationArgumentMaximum = 100;
|
|
36
|
+
export const defaultPaginationArgumentMinimum = 1;
|
|
38
37
|
/**
|
|
39
38
|
* Validate whether a user is allowed to execute a certain GraphQL operation.
|
|
40
39
|
*/
|
|
41
|
-
const ResourceLimitationValidationRule = (params) => (context, executionArgs) => {
|
|
40
|
+
export const ResourceLimitationValidationRule = (params) => (context, executionArgs) => {
|
|
42
41
|
const { paginationArgumentMaximum, paginationArgumentMinimum } = params;
|
|
43
42
|
const nodeCostStack = [];
|
|
44
43
|
let totalNodeCost = 0;
|
|
@@ -128,7 +127,7 @@ const ResourceLimitationValidationRule = (params) => (context, executionArgs) =>
|
|
|
128
127
|
},
|
|
129
128
|
};
|
|
130
129
|
};
|
|
131
|
-
const useResourceLimitations = (params) => {
|
|
130
|
+
export const useResourceLimitations = (params) => {
|
|
132
131
|
var _a, _b, _c, _d;
|
|
133
132
|
const paginationArgumentMaximum = (_a = params === null || params === void 0 ? void 0 : params.paginationArgumentMaximum) !== null && _a !== void 0 ? _a : defaultPaginationArgumentMaximum;
|
|
134
133
|
const paginationArgumentMinimum = (_b = params === null || params === void 0 ? void 0 : params.paginationArgumentMinimum) !== null && _b !== void 0 ? _b : defaultPaginationArgumentMinimum;
|
|
@@ -174,5 +173,3 @@ const useResourceLimitations = (params) => {
|
|
|
174
173
|
},
|
|
175
174
|
};
|
|
176
175
|
};
|
|
177
|
-
|
|
178
|
-
export { ResourceLimitationValidationRule, defaultNodeCostLimit, defaultPaginationArgumentMaximum, defaultPaginationArgumentMinimum, useResourceLimitations };
|
package/package.json
CHANGED
|
@@ -1,38 +1,59 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@envelop/resource-limitations",
|
|
3
|
-
"version": "2.4.0-alpha-
|
|
3
|
+
"version": "2.4.0-alpha-385b86b.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-385b86b.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-385b86b.0",
|
|
12
12
|
"@graphql-tools/utils": "^8.6.1"
|
|
13
13
|
},
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
16
|
-
"url": "https://github.com/
|
|
16
|
+
"url": "https://github.com/n1ru4l/envelop.git",
|
|
17
17
|
"directory": "packages/plugins/resource-limitations"
|
|
18
18
|
},
|
|
19
19
|
"author": "Laurin Quast <laurinquast@googlemail.com>",
|
|
20
20
|
"license": "MIT",
|
|
21
|
-
"main": "index.js",
|
|
22
|
-
"module": "index.
|
|
23
|
-
"typings": "index.d.ts",
|
|
21
|
+
"main": "cjs/index.js",
|
|
22
|
+
"module": "esm/index.js",
|
|
23
|
+
"typings": "typings/index.d.ts",
|
|
24
24
|
"typescript": {
|
|
25
|
-
"definition": "index.d.ts"
|
|
25
|
+
"definition": "typings/index.d.ts"
|
|
26
26
|
},
|
|
27
|
+
"type": "module",
|
|
27
28
|
"exports": {
|
|
28
29
|
".": {
|
|
29
|
-
"require":
|
|
30
|
-
|
|
30
|
+
"require": {
|
|
31
|
+
"types": "./typings/index.d.ts",
|
|
32
|
+
"default": "./cjs/index.js"
|
|
33
|
+
},
|
|
34
|
+
"import": {
|
|
35
|
+
"types": "./typings/index.d.ts",
|
|
36
|
+
"default": "./esm/index.js"
|
|
37
|
+
},
|
|
38
|
+
"default": {
|
|
39
|
+
"types": "./typings/index.d.ts",
|
|
40
|
+
"default": "./esm/index.js"
|
|
41
|
+
}
|
|
31
42
|
},
|
|
32
43
|
"./*": {
|
|
33
|
-
"require":
|
|
34
|
-
|
|
44
|
+
"require": {
|
|
45
|
+
"types": "./typings/*.d.ts",
|
|
46
|
+
"default": "./cjs/*.js"
|
|
47
|
+
},
|
|
48
|
+
"import": {
|
|
49
|
+
"types": "./typings/*.d.ts",
|
|
50
|
+
"default": "./esm/*.js"
|
|
51
|
+
},
|
|
52
|
+
"default": {
|
|
53
|
+
"types": "./typings/*.d.ts",
|
|
54
|
+
"default": "./esm/*.js"
|
|
55
|
+
}
|
|
35
56
|
},
|
|
36
57
|
"./package.json": "./package.json"
|
|
37
58
|
}
|
|
38
|
-
}
|
|
59
|
+
}
|
|
@@ -41,5 +41,5 @@ declare type UseResourceLimitationsParams = {
|
|
|
41
41
|
*/
|
|
42
42
|
extensions?: boolean;
|
|
43
43
|
};
|
|
44
|
-
export declare const useResourceLimitations: (params?: UseResourceLimitationsParams
|
|
44
|
+
export declare const useResourceLimitations: (params?: UseResourceLimitationsParams) => Plugin;
|
|
45
45
|
export {};
|
package/README.md
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
## `@envelop/resource-limitations`
|
|
2
|
-
|
|
3
|
-
This plugins uses `extended-valiations` concept ([details here](https://github.com/dotansimha/envelop/tree/main/packages/plugins/extended-validation#envelopextended-validation)) for implemeting a resource-limitations rate-limit similar to GitHub GraphQL API (see https://docs.github.com/en/graphql/overview/resource-limitations for more details)
|
|
4
|
-
|
|
5
|
-
## Getting Started
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
yarn add @envelop/resource-limitations
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Usage Example
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
import { envelop } from '@envelop/core';
|
|
15
|
-
import { useResourceLimitations } from '@envelop/resource-limitations';
|
|
16
|
-
|
|
17
|
-
const getEnveloped = envelop({
|
|
18
|
-
plugins: [
|
|
19
|
-
// ... other plugins ...
|
|
20
|
-
useResourceLimitations({
|
|
21
|
-
nodeCostLimit: 500000, // optional, default to 500000
|
|
22
|
-
paginationArgumentMaximum: 100, // optional, default to 100
|
|
23
|
-
paginationArgumentMinimum: 1, // optional, default to 1
|
|
24
|
-
paginationArgumentScalars: ['ConnectionInt'], // optional, use if connections use a different scalar type as the argument instead of `Int`
|
|
25
|
-
extensions: false, // set this to `true` in order to add the calculated const to the response of queries
|
|
26
|
-
}),
|
|
27
|
-
],
|
|
28
|
-
});
|
|
29
|
-
```
|