PossibleFragmentSpreads.js.flow 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. // @flow strict
  2. import inspect from '../../jsutils/inspect';
  3. import { GraphQLError } from '../../error/GraphQLError';
  4. import { type ASTVisitor } from '../../language/visitor';
  5. import { isCompositeType } from '../../type/definition';
  6. import { typeFromAST } from '../../utilities/typeFromAST';
  7. import { doTypesOverlap } from '../../utilities/typeComparators';
  8. import { type ValidationContext } from '../ValidationContext';
  9. export function typeIncompatibleSpreadMessage(
  10. fragName: string,
  11. parentType: string,
  12. fragType: string,
  13. ): string {
  14. return `Fragment "${fragName}" cannot be spread here as objects of type "${parentType}" can never be of type "${fragType}".`;
  15. }
  16. export function typeIncompatibleAnonSpreadMessage(
  17. parentType: string,
  18. fragType: string,
  19. ): string {
  20. return `Fragment cannot be spread here as objects of type "${parentType}" can never be of type "${fragType}".`;
  21. }
  22. /**
  23. * Possible fragment spread
  24. *
  25. * A fragment spread is only valid if the type condition could ever possibly
  26. * be true: if there is a non-empty intersection of the possible parent types,
  27. * and possible types which pass the type condition.
  28. */
  29. export function PossibleFragmentSpreads(
  30. context: ValidationContext,
  31. ): ASTVisitor {
  32. return {
  33. InlineFragment(node) {
  34. const fragType = context.getType();
  35. const parentType = context.getParentType();
  36. if (
  37. isCompositeType(fragType) &&
  38. isCompositeType(parentType) &&
  39. !doTypesOverlap(context.getSchema(), fragType, parentType)
  40. ) {
  41. context.reportError(
  42. new GraphQLError(
  43. typeIncompatibleAnonSpreadMessage(
  44. inspect(parentType),
  45. inspect(fragType),
  46. ),
  47. node,
  48. ),
  49. );
  50. }
  51. },
  52. FragmentSpread(node) {
  53. const fragName = node.name.value;
  54. const fragType = getFragmentType(context, fragName);
  55. const parentType = context.getParentType();
  56. if (
  57. fragType &&
  58. parentType &&
  59. !doTypesOverlap(context.getSchema(), fragType, parentType)
  60. ) {
  61. context.reportError(
  62. new GraphQLError(
  63. typeIncompatibleSpreadMessage(
  64. fragName,
  65. inspect(parentType),
  66. inspect(fragType),
  67. ),
  68. node,
  69. ),
  70. );
  71. }
  72. },
  73. };
  74. }
  75. function getFragmentType(context, name) {
  76. const frag = context.getFragment(name);
  77. if (frag) {
  78. const type = typeFromAST(context.getSchema(), frag.typeCondition);
  79. if (isCompositeType(type)) {
  80. return type;
  81. }
  82. }
  83. }