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

Add root level resolvers support to avoidOptionals #10077

Merged
merged 4 commits into from
Aug 3, 2024

Conversation

eddeee888
Copy link
Collaborator

Description

Most (if not all of the time), we need to implement root-level resolvers, otherwise there'd be runtime errors.
This PR extends avoidOptionals to generate non-optional root-level resolvers.

This will also be used in Server Preset to avoid having to use NonNullable as discussed here

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

How Has This Been Tested?

  • Unit test

Copy link

changeset-bot bot commented Aug 1, 2024

🦋 Changeset detected

Latest commit: 0e5f88a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 9 packages
Name Type
@graphql-codegen/visitor-plugin-common Minor
@graphql-codegen/typescript-operations Minor
@graphql-codegen/typescript Minor
@graphql-codegen/typescript-resolvers Minor
@graphql-codegen/typescript-document-nodes Patch
@graphql-codegen/gql-tag-operations Patch
@graphql-codegen/typed-document-node Patch
@graphql-codegen/graphql-modules-preset Patch
@graphql-codegen/client-preset Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR


export const DEFAULT_AVOID_OPTIONALS: AvoidOptionalsConfig = {
export const DEFAULT_AVOID_OPTIONALS: NormalizedAvoidOptionalsConfig = {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

NormalizedAvoidOptionalsConfig is similar to AvoidOptionalsConfig but all the fields are non-optional, making it much easier to code with

Copy link
Contributor

github-actions bot commented Aug 1, 2024

🚀 Snapshot Release (alpha)

The latest changes of this PR are available as alpha on npm (based on the declared changesets):

Package Version Info
@graphql-codegen/visitor-plugin-common 5.4.0-alpha-20240801152800-0e5f88a94d303ef7dd966bd20c3905047e9dd61f npm ↗︎ unpkg ↗︎
@graphql-codegen/typescript-document-nodes 4.0.10-alpha-20240801152800-0e5f88a94d303ef7dd966bd20c3905047e9dd61f npm ↗︎ unpkg ↗︎
@graphql-codegen/gql-tag-operations 4.0.10-alpha-20240801152800-0e5f88a94d303ef7dd966bd20c3905047e9dd61f npm ↗︎ unpkg ↗︎
@graphql-codegen/typescript-operations 4.3.0-alpha-20240801152800-0e5f88a94d303ef7dd966bd20c3905047e9dd61f npm ↗︎ unpkg ↗︎
@graphql-codegen/typescript-resolvers 4.3.0-alpha-20240801152800-0e5f88a94d303ef7dd966bd20c3905047e9dd61f npm ↗︎ unpkg ↗︎
@graphql-codegen/typed-document-node 5.0.10-alpha-20240801152800-0e5f88a94d303ef7dd966bd20c3905047e9dd61f npm ↗︎ unpkg ↗︎
@graphql-codegen/typescript 4.1.0-alpha-20240801152800-0e5f88a94d303ef7dd966bd20c3905047e9dd61f npm ↗︎ unpkg ↗︎
@graphql-codegen/client-preset 4.4.0-alpha-20240801152800-0e5f88a94d303ef7dd966bd20c3905047e9dd61f npm ↗︎ unpkg ↗︎
@graphql-codegen/graphql-modules-preset 4.0.10-alpha-20240801152800-0e5f88a94d303ef7dd966bd20c3905047e9dd61f npm ↗︎ unpkg ↗︎

@@ -1307,7 +1314,7 @@ export class BaseResolversVisitor<
}

protected formatRootResolver(schemaTypeName: string, resolverType: string, declarationKind: DeclarationKind): string {
return `${schemaTypeName}${this.config.avoidOptionals ? '' : '?'}: ${resolverType}${this.getPunctuation(
return `${schemaTypeName}${this.config.avoidOptionals.resolvers ? '' : '?'}: ${resolverType}${this.getPunctuation(
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The old implementation can cause a scenario where an empty object is passed in, but it'd be treated as truthy so it'd treat it as non-optional.

In this new implementation, we use resolvers because it's the option that is used for all resolvers-related fields in the past.

@@ -682,7 +689,7 @@ export class BaseResolversVisitor<
allResolversTypeName: getConfigValue(rawConfig.allResolversTypeName, 'Resolvers'),
rootValueType: parseMapper(rawConfig.rootValueType || '{}', 'RootValueType'),
namespacedImportName: getConfigValue(rawConfig.namespacedImportName, ''),
avoidOptionals: getConfigValue(rawConfig.avoidOptionals, false),
avoidOptionals: normalizeAvoidOptionals(rawConfig.avoidOptionals),
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This plugin seems to be the only one that's not using normalizeAvoidOptionals. This makes it consistent.

Copy link
Contributor

github-actions bot commented Aug 1, 2024

💻 Website Preview

The latest changes are available as preview in: https://32b7e480.graphql-code-generator.pages.dev

@@ -78,6 +80,8 @@ export interface ParsedResolversConfig extends ParsedConfig {
resolversNonOptionalTypename: ResolversNonOptionalTypenameConfig;
}

type FieldDefinitionPrintFn = (parentName: string, avoidResolverOptionals: boolean) => string | null;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was REALLY confused why/how the field's name is a function. Found out it's because we do custom logic in FieldDefinition function. I'm extracting the type here to be able to type it instead of using any

Comment on lines -1466 to -1469
const avoidResolverOptionals =
typeof this.config.avoidOptionals === 'object'
? this.config.avoidOptionals?.resolvers
: this.config.avoidOptionals === true;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We now pass this from caller. I found that this function is called in Interface fields and Object type fields

return false;
})();

const fieldsContent = (node.fields as unknown as FieldDefinitionPrintFn[]).map(f => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I have to inline node.fields as unknown as FieldDefinitionPrintFn[] here because it's the easiest way to communicate where this function comes from. If there's a better way, let me know!

@@ -1720,21 +1736,23 @@ export class BaseResolversVisitor<
const allTypesMap = this._schema.getTypeMap();
const implementingTypes: string[] = [];

this._collectedResolvers[node.name as any] = {
const typeName = node.name as any as string;
Copy link
Collaborator Author

@eddeee888 eddeee888 Aug 1, 2024

Choose a reason for hiding this comment

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

Cleaning this up by limiting where we do as any as string in this function

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There's quite a few tests related to avoidOptionals so I'm creating this new file to keep the old ones and add new tests related to query, mutation and subscription.

@eddeee888 eddeee888 requested review from n1ru4l and saihaj August 2, 2024 05:23
Copy link
Collaborator

@n1ru4l n1ru4l left a comment

Choose a reason for hiding this comment

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

👍 nice

@eddeee888 eddeee888 merged commit 3f4f546 into master Aug 3, 2024
19 checks passed
@eddeee888 eddeee888 deleted the add-non-optional-root-level-resolver branch August 3, 2024 04:27
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.

3 participants