Skip to content
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

feat: limit number of items of aql queries for queries and mutations … #318

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Etienne-Buschong
Copy link
Contributor

…(wip)

@Etienne-Buschong Etienne-Buschong force-pushed the limit-root-entities-query-and-mutation branch from 80443ed to f568a50 Compare May 15, 2024 08:00
@Etienne-Buschong Etienne-Buschong force-pushed the limit-root-entities-query-and-mutation branch from f568a50 to 54e45be Compare May 16, 2024 07:50
@Etienne-Buschong
Copy link
Contributor Author

@Yogu Is there anything left from your side that I should adapt?
Otherwise I think this MR is ready.

@Yogu
Copy link
Member

Yogu commented May 24, 2024

@Yogu Is there anything left from your side that I should adapt? Otherwise I think this MR is ready.

currently reviewing it

Comment on lines +46 to +47
const MANUAL_MAX_COUNT_UPPER_BOUND = 50000;
const DEFAULT_MAX_COUNT_UPPER_BOUND = 10000;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you make these configurable via ExecutionOptions?

for MANUAL_MAX_COUNT_UPPER_BOUND, we could call the option something like maxLimitForRootEntityQueries.

for DEFAULT_MAX_COUNT_UPPER_BOUND, we could call it something like implicitLimitForRootEntityQueries

not 100% sure about the names. "first" would be closer than "limit", but I don't think you would think of the argument if you read "maxFirst" or "maxFirstValue"...

the options should document which queries this limit applies to and that it will throw an error if the limit is exceeded.

It should be possible to turn both features off, I would even turn them off by default (if kept as undefined)

const MANUAL_MAX_COUNT_UPPER_BOUND = 50000;
const DEFAULT_MAX_COUNT_UPPER_BOUND = 10000;

export type LimitTypeCheckType = 'ResultValidator' | 'Resolver';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cruddl usually uses enums instead of string unions

Comment on lines +19 to +25
if (isRuntimeErrorValue(value)) {
throw extractRuntimeError(value);
}

if (transformResult) {
return transformResult(value, args);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could swap the two (and run them one after the other), then you would be able to swallow or create runtime errors in the transformer

Comment on lines +38 to +40
* Will be called in the actual graphql field resolver for this node.
* Can be used to transform and validate a fields data after query-execution.
*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Will be called in the actual graphql field resolver for this node.
* Can be used to transform and validate a fields data after query-execution.
*
* Will be called in the actual graphql field resolver for this node.
* Can be used to transform and validate a fields data after query-execution.
*
* Needs be synchronous, it is not possible to return a promise here.
*

because in a regular resolver, you would be able to return a promise. For performance reasons it's probably better to not add the await while we don't need it

maxCount > MANUAL_MAX_COUNT_UPPER_BOUND
) {
return new RuntimeErrorQueryNode(
`The requested number of elements via the \"first\" parameter (${maxCount}) exceeds the maximum limit of ${MANUAL_MAX_COUNT_UPPER_BOUND}. Reduce the number of items you fetch and try again.`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`The requested number of elements via the \"first\" parameter (${maxCount}) exceeds the maximum limit of ${MANUAL_MAX_COUNT_UPPER_BOUND}. Reduce the number of items you fetch and try again.`,
`The requested number of elements via the \"first\" parameter (${maxCount}) exceeds the maximum limit of ${MANUAL_MAX_COUNT_UPPER_BOUND}.`,

I think the action is obvious from the problem description

) {
return new RuntimeErrorQueryNode(
`The requested number of elements via the \"first\" parameter (${maxCount}) exceeds the maximum limit of ${MANUAL_MAX_COUNT_UPPER_BOUND}. Reduce the number of items you fetch and try again.`,
{ code: NOT_SUPPORTED_ERROR },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOT_SUPPORTED means a feature or is not supported in a specific context. I don't think any of the existing error codes are fitting. You probably don't need to catch this error and handle it specifically - if it occurs, you need to change your page sizes in the code. So maybe a plain ARGUMENT_OUT_OF_RANGE?

throw new RuntimeValidationError(
`Collection is truncated by default to ${validationData.maximumExpectedNumberOfItems} elements but contains more elements than this limit. Specify a limit manually to retrieve all elements of the collection.`,
{
code: validationData.errorCode,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't pass this errorCode in getValidatorData(). But I don't think we need to make it configurable if the message is hard-coded.

regarding the code - I could see TOO_MANY_OBJECTS - we already have FLEX_SEARCH_TOO_MANY_OBJECTS and it's kind of similar.


if (result.length > validationData.maximumExpectedNumberOfItems) {
throw new RuntimeValidationError(
`Collection is truncated by default to ${validationData.maximumExpectedNumberOfItems} elements but contains more elements than this limit. Specify a limit manually to retrieve all elements of the collection.`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not use the term "truncated" here because for the user, nothing is truncated - an error is thrown. Implicit truncation is exactly what we want to avoid doing.

There's also a second reason you might want to specify a limit: if you want a truncated result. So I would include that in the error message.

I think phrasing the error is easier when we know the kind of operation we do

'Query would return more than {limit} objects. Specify "first" to increase the limit or truncate the result.'

'Mutation would return more than {limit} objects. Specify "first" to increase the limit or truncate the result.'

Comment on lines +424 to +426
const fieldWithListArgs = this.listAugmentation.augment(fieldBase, rootEntityType, {
orderByAugmentationOptions: { firstLimitCheckType: 'ResultValidator' },
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also need to apply the logic to generateDeleteAllField (forgot about that when we talked about it). It returns all deleted objects, so we also need to limit it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants