validate-documents.js 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. import { Kind, validate, specifiedRules, concatAST, versionInfo, } from 'graphql';
  2. import { AggregateError } from './AggregateError.js';
  3. export async function validateGraphQlDocuments(schema, documentFiles, effectiveRules = createDefaultRules()) {
  4. const allFragmentMap = new Map();
  5. const documentFileObjectsToValidate = [];
  6. for (const documentFile of documentFiles) {
  7. if (documentFile.document) {
  8. const definitionsToValidate = [];
  9. for (const definitionNode of documentFile.document.definitions) {
  10. if (definitionNode.kind === Kind.FRAGMENT_DEFINITION) {
  11. allFragmentMap.set(definitionNode.name.value, definitionNode);
  12. }
  13. else {
  14. definitionsToValidate.push(definitionNode);
  15. }
  16. }
  17. documentFileObjectsToValidate.push({
  18. location: documentFile.location,
  19. document: {
  20. kind: Kind.DOCUMENT,
  21. definitions: definitionsToValidate,
  22. },
  23. });
  24. }
  25. }
  26. const allErrors = [];
  27. const allFragmentsDocument = {
  28. kind: Kind.DOCUMENT,
  29. definitions: [...allFragmentMap.values()],
  30. };
  31. await Promise.all(documentFileObjectsToValidate.map(async (documentFile) => {
  32. const documentToValidate = concatAST([allFragmentsDocument, documentFile.document]);
  33. const errors = validate(schema, documentToValidate, effectiveRules);
  34. if (errors.length > 0) {
  35. allErrors.push({
  36. filePath: documentFile.location,
  37. errors,
  38. });
  39. }
  40. }));
  41. return allErrors;
  42. }
  43. export function checkValidationErrors(loadDocumentErrors) {
  44. if (loadDocumentErrors.length > 0) {
  45. const errors = [];
  46. for (const loadDocumentError of loadDocumentErrors) {
  47. for (const graphQLError of loadDocumentError.errors) {
  48. const error = new Error();
  49. error.name = 'GraphQLDocumentError';
  50. error.message = `${error.name}: ${graphQLError.message}`;
  51. error.stack = error.message;
  52. if (graphQLError.locations) {
  53. for (const location of graphQLError.locations) {
  54. error.stack += `\n at ${loadDocumentError.filePath}:${location.line}:${location.column}`;
  55. }
  56. }
  57. errors.push(error);
  58. }
  59. }
  60. throw new AggregateError(errors, `GraphQL Document Validation failed with ${errors.length} errors;
  61. ${errors.map((error, index) => `Error ${index}: ${error.stack}`).join('\n\n')}`);
  62. }
  63. }
  64. export function createDefaultRules() {
  65. let ignored = ['NoUnusedFragmentsRule', 'NoUnusedVariablesRule', 'KnownDirectivesRule'];
  66. if (versionInfo.major < 15) {
  67. ignored = ignored.map(rule => rule.replace(/Rule$/, ''));
  68. }
  69. return specifiedRules.filter((f) => !ignored.includes(f.name));
  70. }