-
-
Notifications
You must be signed in to change notification settings - Fork 677
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Query complexity #139
Query complexity #139
Conversation
Codecov Report
@@ Coverage Diff @@
## master #139 +/- ##
==========================================
+ Coverage 95.08% 95.09% +0.01%
==========================================
Files 60 60
Lines 956 958 +2
Branches 170 170
==========================================
+ Hits 909 911 +2
Misses 46 46
Partials 1 1
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- What about queries, mutations and subscriptions? Please add proper changes also to them.
query-complexity
is not asimple-usage
- please create a separate example folder with comments in code how and why we setupvalidationRules
- no implicit any - please describe the types of
graphql-query-complexity
module in the way that we use, along with interfaces merging to extendGraphQLFieldConfig
interface - add proper tests for this feature - registering in metadata storage, as well as simple check if the validation works (one query that pass, one that fails)
- It might be useful to add proper section in docs about this integration, how to use it, etc.
I think I need to make a list of all things that PRs have to provide to be merged 😉
src/decorators/types.ts
Outdated
@@ -17,6 +17,7 @@ export type SubscriptionFilterFunc = ( | |||
|
|||
export interface DecoratorTypeOptions { | |||
nullable?: boolean; | |||
complexity?: number; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
graphql-query-complexity
allows to calculate it dynamically: complexity: (args, childComplexity) => childComplexity * args.count,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add tests also for queries and field resolver as they dynamically add fields to object types too.
Also please create proper documentation of this feature in advanced section, with tutorial how to use it and setup, just like authorization have.
@@ -0,0 +1,35 @@ | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that this might become obsolete soon 😕
slicknode/graphql-query-complexity@f7713b2
We should just wait with this PR for publishing that package with types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I compiled his code in TSC and put the types. So it would be similar when he publishes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Partially - newest version is flattened to a single file, so there will be no graphql-query-complexity/dist/QueryComplexity
and also it's weird to have typings on repo rather than publish it on DefinitelyTyped. So let's wait a few days and decide what @ivome will do 😉
examples/query-complexity/index.ts
Outdated
* More doucmentation can be found (here)[https://github.com/ivome/graphql-query-complexity] | ||
*/ | ||
queryComplexity({ | ||
maximumComplexity: 8, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here we can declare the maximum cost of the queries, it can be dynamically and depend on user privileges
examples/query-complexity/index.ts
Outdated
*/ | ||
queryComplexity({ | ||
maximumComplexity: 8, | ||
variables: req.query.variables, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we also have to pass the query variables to the queryComplexity
validator
examples/query-complexity/index.ts
Outdated
maximumComplexity: 8, | ||
variables: req.query.variables, | ||
onComplete: (complexity: number) => { | ||
console.log("Query Complexity:", complexity); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can also react on complexity calculation and do something (@abhikmitra I don't know why we show example with console.log btw 😆)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure what do you have in your mind other than console log ? I just followed the same format as what is followed in graphql-query-complexity repo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think here you can subtract the amount from user's account points fund.
examples/simple-usage/index.ts
Outdated
@@ -18,6 +18,15 @@ async function bootstrap() { | |||
port: 4000, | |||
endpoint: "/graphql", | |||
playground: "/playground", | |||
validationRules: req => [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this from simple-usage
src/schema/schema-generator.ts
Outdated
@@ -227,6 +227,7 @@ export abstract class SchemaGenerator { | |||
); | |||
fieldsMap[field.schemaName] = { | |||
type: this.getGraphQLOutputType(field.name, field.getType(), field.typeOptions), | |||
complexity: field.complexity || 1, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we explicitly put 1
here? Isn't it default behavior of graphql-query-complexity
?
a4b2289
to
7357a86
Compare
@19majkel94 So all fieldResolvers will always have a corresponding field type right ? Complexity is respected on field type and would not be respected on @fieldResolvers . Is that ok ? Should we stop allowing complexity on field resolvers ? |
@abhikmitra And now with the fresh eyesight, my design of metadata building is awful 🤢 |
Fixed your comments . Let me know what else you need. |
docs/complexity.md
Outdated
}); | ||
``` | ||
|
||
And it's done! 😉 . You can pass complexity as option to any of `@field`, `@fieldResolver`, `@mutation` & `@subscription`. For the same property, if both the @fieldresolver as well as @field have complexity defined , then the complexity passed to the field resolver decorator takes precedence. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PascalCase for decorators names and always wrap in backticks
@Field({ complexity: 6 }) | ||
title: string; | ||
|
||
@Field(type => String, { nullable: true, deprecationReason: "Use `description` field instead" }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove also all this not needed fields - keep focus only on complexity
feature, the Recipe is just the background.
examples/simple-usage/index.ts
Outdated
@@ -1,7 +1,7 @@ | |||
import "reflect-metadata"; | |||
import { GraphQLServer, Options } from "graphql-yoga"; | |||
import { buildSchema } from "../../src"; | |||
|
|||
import queryComplexity from "graphql-query-complexity"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is not needed.
tests/functional/fields.ts
Outdated
@@ -88,6 +91,13 @@ describe("Fields - schema", () => { | |||
expect(schemaIntrospection).toBeDefined(); | |||
}); | |||
|
|||
it("should add complexity info to the metadata storage", async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it should register complexity info for field
tests/functional/resolvers.ts
Outdated
fieldResolverMethod | ||
} | ||
}`; | ||
const ast = parse(query); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you create a helper function in this describe
suite to avoid duplication in this three it
test cases?
docs/complexity.md
Outdated
|
||
And it's done! 😉 . You can pass complexity as option to any of `@field`, `@fieldResolver`, `@mutation` & `@subscription`. For the same property, if both the @fieldresolver as well as @field have complexity defined , then the complexity passed to the field resolver decorator takes precedence. | ||
|
||
Have a look at the [graphql-query-complexity](https://github.com/ivome/graphql-query-complexity) docs to understand more about how query complexity is computed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For more info about how query complexity is computed, please visit graphql-query-complexity
docs.
docs/complexity.md
Outdated
}); | ||
``` | ||
|
||
And it's done! 😉 . You can pass complexity as option to any of `@field`, `@fieldResolver`, `@mutation` & `@subscription`. For the same property, if both the @fieldresolver as well as @field have complexity defined , then the complexity passed to the field resolver decorator takes precedence. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move You can pass complexity as option to any...
above this, place it below You can omit the complexity
.
docs/complexity.md
Outdated
@Field({ complexity: 2}) | ||
publicField: string; | ||
|
||
@Field({ complexity: 3 }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe show a field with args and show how to use complexityResolver?
docs/complexity.md
Outdated
--- | ||
title: Query Complexity | ||
--- | ||
A single GraphQL query can potentially generate thousands of database operations. To keep track and limit of what each graphQl operation can do , `TypeGraphQL` provides you the option of integrating with Query Complexity tools like [graphql-query-complexity](https://github.com/ivome/graphql-query-complexity). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- fix
graphQl
- ...can potentially generate huge workload for a server, like thousands of database operations.
- maybe we should also mention DDoS and recursive queries, like
user -> posts (1000) -> user -> posts (1000)
@abhikmitra |
Sorry for the late reply. I just release a new version of graphql-query-complexity with some major changes, deprecations and new features, type definitions in the package etc. |
@ivome Thanks! 🎉 @abhikmitra Please check new features of |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@19majkel94 I upgraded to 0.2.0 |
docs/complexity.md
Outdated
@@ -1,12 +1,12 @@ | |||
--- | |||
title: Query Complexity | |||
--- | |||
A single graphQl query can potentially generate huge workload for a server, like thousands of database operations which can be used to cause DDoS attacks. To keep track and limit of what each graphQl operation can do , `TypeGraphQL` provides you the option of integrating with Query Complexity tools like [graphql-query-complexity](https://github.com/ivome/graphql-query-complexity). | |||
A single GraphQl query can potentially generate huge workload for a server, like thousands of database operations which can be used to cause DDoS attacks. To keep track and limit of what each GraphQl operation can do , `TypeGraphQL` provides you the option of integrating with Query Complexity tools like [graphql-query-complexity](https://github.com/ivome/graphql-query-complexity). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it really to hard to write GraphQL
? Copy and paste? 😆
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
docs/complexity.md
Outdated
@@ -19,15 +19,15 @@ class MyObject { | |||
@Field({ complexity: 2}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
space - complexity: 2 })
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
docs/complexity.md
Outdated
@@ -51,6 +51,15 @@ In next step, you need to integrate `graphql-query-complexity` with your graphql | |||
onComplete: (complexity: number) => { | |||
console.log("Query Complexity:", complexity); | |||
}, | |||
estimators: [ | |||
fieldConfigEstimator(), | |||
// Add more estimators here... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not a good suggestion - which one, directives?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
docs/complexity.md
Outdated
@@ -51,6 +51,15 @@ In next step, you need to integrate `graphql-query-complexity` with your graphql | |||
onComplete: (complexity: number) => { | |||
console.log("Query Complexity:", complexity); | |||
}, | |||
estimators: [ | |||
fieldConfigEstimator(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here should be comment that this one is mandatory to make it works
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
docs/complexity.md
Outdated
// Add more estimators here... | ||
// This will assign each field a complexity of 1 if no other estimator | ||
// returned a value. | ||
simpleEstimator({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And that here we can define the default value for field not explicitly annotated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/decorators/types.ts
Outdated
@@ -32,7 +32,7 @@ export interface ValidateOptions { | |||
validate?: boolean | ValidatorOptions; | |||
} | |||
export interface ComplexityOptions { | |||
complexity?: Complexity; | |||
complexity?: ((arg: ComplexityEstimatorArgs) => number) | number; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a ComplexityEstimator
type that we should use - complexity
function can return void
to fallback to default complexity
src/schema/schema-generator.ts
Outdated
@@ -360,7 +360,7 @@ export abstract class SchemaGenerator { | |||
description: handler.description, | |||
deprecationReason: handler.deprecationReason, | |||
complexity: handler.complexity, | |||
}; | |||
} as GraphQLFieldConfig<any, any>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reason of that? Declaration merging isn't working?
https://github.com/slicknode/graphql-query-complexity/blob/157285004e22eed3ca1ab1b43226d671f0c3affb/src/QueryComplexity.ts#L32-L36
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@abhikmitra |
@19majkel94 anything else ? |
@abhikmitra Thank you very much for your contribution! ❤️ |
Fixes #36
Adds an option parameter called complexity to the field decorator.