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(graphql-parse-resolve-info): add isResolveTree type guard #858

Open
wants to merge 1 commit into
base: v4
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions packages/graphql-parse-resolve-info/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,26 @@ single-level object containing only the fields compatible with
...as before...
```

### `isResolveTree(value)`

Determines whether the argument is a value of type `ResolveTree`. This is useful only to TypeScript users. It allows to differentiate between `ResolveTree` and `FieldsByTypeName` objects to pass to `simplifyParsedResolveInfoFragmentWithType `.

Example:

```ts
import { isResolveTree } from "graphql-parse-resolve-info";

const { parsedResolveInfoFragment, simplifiedFragment } = await graphql(
Schema,
query
);
Comment on lines +384 to +387
Copy link
Member

Choose a reason for hiding this comment

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

I think you might have mis-typed this example, we wouldn't get these results from calling graphql()?

Maybe something like this would be more helpful?

Suggested change
const { parsedResolveInfoFragment, simplifiedFragment } = await graphql(
Schema,
query
);
const myResolver: GraphQLFieldResolver<any, any> = (source, args, context, resolveInfo) => {
const parsedResolveInfoFragment = parseResolveInfo(resolveInfo);
if (!isResolveTree(parsedResolveInfoFragment)) {
throw new Error(`Expected ResolveTree`);
}
const simplifiedFragment = simplifyParsedResolveInfoFragmentWithType(
parsedResolveInfoFragment,
resolveInfo.returnType
);
// ...
};


isResolveTree(parsedResolveInfoFragment); // returns true
isResolveTree(simplifiedFragment); // returns true
isResolveTree(parsedResolveInfoFragment.fieldsByTypeName); // returns false
isResolveTree(simplifiedFragment.fieldsByTypeName); // returns false
```

## Thanks

This project was originally based on https://github.com/tjmehta/graphql-parse-fields, but has evolved a lot since then.
39 changes: 39 additions & 0 deletions packages/graphql-parse-resolve-info/__tests__/test.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const {
parseResolveInfo,
simplifyParsedResolveInfoFragmentWithType,
isResolveTree,
} = require("../src");
const {
graphql,
Expand Down Expand Up @@ -276,3 +277,41 @@ test("directives", async () => {
expect(parsedResolveInfoFragment).toMatchSnapshot();
expect(simplifiedFragment).toMatchSnapshot();
});

test("isResolveTree", async () => {
const variables = {
include: true,
exclude: false,
};
const { parsedResolveInfoFragment, simplifiedFragment } = await new Promise(
(resolve, reject) => {
let o;
graphql(
Schema,
query,
null,
{
test: _o => (o = _o),
},
variables
).then(d => {
try {
const { errors } = d;
expect(errors).toBeFalsy();
} catch (e) {
return reject(e);
}
if (o) {
resolve(o);
} else {
reject(new Error("test not called?"));
}
}, reject);
}
);

expect(isResolveTree(parsedResolveInfoFragment)).toBe(true);
expect(isResolveTree(simplifiedFragment)).toBe(true);
expect(isResolveTree(parsedResolveInfoFragment.fieldsByTypeName)).toBe(false);
expect(isResolveTree(simplifiedFragment.fieldsByTypeName)).toBe(false);
});
6 changes: 6 additions & 0 deletions packages/graphql-parse-resolve-info/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ export interface ResolveTree {
fieldsByTypeName: FieldsByTypeName;
}

export function isResolveTree(
value: ResolveTree | FieldsByTypeName | null | undefined
): value is ResolveTree {
return typeof value?.name === "string" && Boolean(value.fieldsByTypeName);
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure this is safe; depending on how it's used it could be tricked by a query like:

{
  field {
    name: id # `ID` type is always a string
    fieldsByTypeName: __typename
  }
}

}

const debug = debugFactory("graphql-parse-resolve-info");

const DEBUG_ENABLED = debug.enabled;
Expand Down