buildClientSchema.mjs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. import objectValues from '../polyfills/objectValues';
  2. import inspect from '../jsutils/inspect';
  3. import devAssert from '../jsutils/devAssert';
  4. import keyValMap from '../jsutils/keyValMap';
  5. import isObjectLike from '../jsutils/isObjectLike';
  6. import { parseValue } from '../language/parser';
  7. import { GraphQLDirective } from '../type/directives';
  8. import { specifiedScalarTypes } from '../type/scalars';
  9. import { introspectionTypes, TypeKind } from '../type/introspection';
  10. import { GraphQLSchema } from '../type/schema';
  11. import { isInputType, isOutputType, GraphQLScalarType, GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType, GraphQLEnumType, GraphQLInputObjectType, GraphQLList, GraphQLNonNull, assertNullableType, assertObjectType, assertInterfaceType } from '../type/definition';
  12. import { valueFromAST } from './valueFromAST';
  13. /**
  14. * Build a GraphQLSchema for use by client tools.
  15. *
  16. * Given the result of a client running the introspection query, creates and
  17. * returns a GraphQLSchema instance which can be then used with all graphql-js
  18. * tools, but cannot be used to execute a query, as introspection does not
  19. * represent the "resolver", "parse" or "serialize" functions or any other
  20. * server-internal mechanisms.
  21. *
  22. * This function expects a complete introspection result. Don't forget to check
  23. * the "errors" field of a server response before calling this function.
  24. */
  25. export function buildClientSchema(introspection, options) {
  26. isObjectLike(introspection) && isObjectLike(introspection.__schema) || devAssert(0, 'Invalid or incomplete introspection result. Ensure that you are passing "data" property of introspection response and no "errors" was returned alongside: ' + inspect(introspection)); // Get the schema from the introspection result.
  27. var schemaIntrospection = introspection.__schema; // Iterate through all types, getting the type definition for each.
  28. var typeMap = keyValMap(schemaIntrospection.types, function (typeIntrospection) {
  29. return typeIntrospection.name;
  30. }, function (typeIntrospection) {
  31. return buildType(typeIntrospection);
  32. });
  33. for (var _i2 = 0, _ref2 = [].concat(specifiedScalarTypes, introspectionTypes); _i2 < _ref2.length; _i2++) {
  34. var stdType = _ref2[_i2];
  35. if (typeMap[stdType.name]) {
  36. typeMap[stdType.name] = stdType;
  37. }
  38. } // Get the root Query, Mutation, and Subscription types.
  39. var queryType = schemaIntrospection.queryType ? getObjectType(schemaIntrospection.queryType) : null;
  40. var mutationType = schemaIntrospection.mutationType ? getObjectType(schemaIntrospection.mutationType) : null;
  41. var subscriptionType = schemaIntrospection.subscriptionType ? getObjectType(schemaIntrospection.subscriptionType) : null; // Get the directives supported by Introspection, assuming empty-set if
  42. // directives were not queried for.
  43. var directives = schemaIntrospection.directives ? schemaIntrospection.directives.map(buildDirective) : []; // Then produce and return a Schema with these types.
  44. return new GraphQLSchema({
  45. query: queryType,
  46. mutation: mutationType,
  47. subscription: subscriptionType,
  48. types: objectValues(typeMap),
  49. directives: directives,
  50. assumeValid: options && options.assumeValid,
  51. allowedLegacyNames: options && options.allowedLegacyNames
  52. }); // Given a type reference in introspection, return the GraphQLType instance.
  53. // preferring cached instances before building new instances.
  54. function getType(typeRef) {
  55. if (typeRef.kind === TypeKind.LIST) {
  56. var itemRef = typeRef.ofType;
  57. if (!itemRef) {
  58. throw new Error('Decorated type deeper than introspection query.');
  59. }
  60. return GraphQLList(getType(itemRef));
  61. }
  62. if (typeRef.kind === TypeKind.NON_NULL) {
  63. var nullableRef = typeRef.ofType;
  64. if (!nullableRef) {
  65. throw new Error('Decorated type deeper than introspection query.');
  66. }
  67. var nullableType = getType(nullableRef);
  68. return GraphQLNonNull(assertNullableType(nullableType));
  69. }
  70. if (!typeRef.name) {
  71. throw new Error('Unknown type reference: ' + inspect(typeRef));
  72. }
  73. return getNamedType(typeRef.name);
  74. }
  75. function getNamedType(typeName) {
  76. var type = typeMap[typeName];
  77. if (!type) {
  78. throw new Error("Invalid or incomplete schema, unknown type: ".concat(typeName, ". Ensure that a full introspection query is used in order to build a client schema."));
  79. }
  80. return type;
  81. }
  82. function getInputType(typeRef) {
  83. var type = getType(typeRef);
  84. if (isInputType(type)) {
  85. return type;
  86. }
  87. throw new Error('Introspection must provide input type for arguments, but received: ' + inspect(type) + '.');
  88. }
  89. function getOutputType(typeRef) {
  90. var type = getType(typeRef);
  91. if (isOutputType(type)) {
  92. return type;
  93. }
  94. throw new Error('Introspection must provide output type for fields, but received: ' + inspect(type) + '.');
  95. }
  96. function getObjectType(typeRef) {
  97. var type = getType(typeRef);
  98. return assertObjectType(type);
  99. }
  100. function getInterfaceType(typeRef) {
  101. var type = getType(typeRef);
  102. return assertInterfaceType(type);
  103. } // Given a type's introspection result, construct the correct
  104. // GraphQLType instance.
  105. function buildType(type) {
  106. if (type && type.name && type.kind) {
  107. switch (type.kind) {
  108. case TypeKind.SCALAR:
  109. return buildScalarDef(type);
  110. case TypeKind.OBJECT:
  111. return buildObjectDef(type);
  112. case TypeKind.INTERFACE:
  113. return buildInterfaceDef(type);
  114. case TypeKind.UNION:
  115. return buildUnionDef(type);
  116. case TypeKind.ENUM:
  117. return buildEnumDef(type);
  118. case TypeKind.INPUT_OBJECT:
  119. return buildInputObjectDef(type);
  120. }
  121. }
  122. throw new Error('Invalid or incomplete introspection result. Ensure that a full introspection query is used in order to build a client schema:' + inspect(type));
  123. }
  124. function buildScalarDef(scalarIntrospection) {
  125. return new GraphQLScalarType({
  126. name: scalarIntrospection.name,
  127. description: scalarIntrospection.description
  128. });
  129. }
  130. function buildObjectDef(objectIntrospection) {
  131. if (!objectIntrospection.interfaces) {
  132. throw new Error('Introspection result missing interfaces: ' + inspect(objectIntrospection));
  133. }
  134. return new GraphQLObjectType({
  135. name: objectIntrospection.name,
  136. description: objectIntrospection.description,
  137. interfaces: function interfaces() {
  138. return objectIntrospection.interfaces.map(getInterfaceType);
  139. },
  140. fields: function fields() {
  141. return buildFieldDefMap(objectIntrospection);
  142. }
  143. });
  144. }
  145. function buildInterfaceDef(interfaceIntrospection) {
  146. return new GraphQLInterfaceType({
  147. name: interfaceIntrospection.name,
  148. description: interfaceIntrospection.description,
  149. fields: function fields() {
  150. return buildFieldDefMap(interfaceIntrospection);
  151. }
  152. });
  153. }
  154. function buildUnionDef(unionIntrospection) {
  155. if (!unionIntrospection.possibleTypes) {
  156. throw new Error('Introspection result missing possibleTypes: ' + inspect(unionIntrospection));
  157. }
  158. return new GraphQLUnionType({
  159. name: unionIntrospection.name,
  160. description: unionIntrospection.description,
  161. types: function types() {
  162. return unionIntrospection.possibleTypes.map(getObjectType);
  163. }
  164. });
  165. }
  166. function buildEnumDef(enumIntrospection) {
  167. if (!enumIntrospection.enumValues) {
  168. throw new Error('Introspection result missing enumValues: ' + inspect(enumIntrospection));
  169. }
  170. return new GraphQLEnumType({
  171. name: enumIntrospection.name,
  172. description: enumIntrospection.description,
  173. values: keyValMap(enumIntrospection.enumValues, function (valueIntrospection) {
  174. return valueIntrospection.name;
  175. }, function (valueIntrospection) {
  176. return {
  177. description: valueIntrospection.description,
  178. deprecationReason: valueIntrospection.deprecationReason
  179. };
  180. })
  181. });
  182. }
  183. function buildInputObjectDef(inputObjectIntrospection) {
  184. if (!inputObjectIntrospection.inputFields) {
  185. throw new Error('Introspection result missing inputFields: ' + inspect(inputObjectIntrospection));
  186. }
  187. return new GraphQLInputObjectType({
  188. name: inputObjectIntrospection.name,
  189. description: inputObjectIntrospection.description,
  190. fields: function fields() {
  191. return buildInputValueDefMap(inputObjectIntrospection.inputFields);
  192. }
  193. });
  194. }
  195. function buildFieldDefMap(typeIntrospection) {
  196. if (!typeIntrospection.fields) {
  197. throw new Error('Introspection result missing fields: ' + inspect(typeIntrospection));
  198. }
  199. return keyValMap(typeIntrospection.fields, function (fieldIntrospection) {
  200. return fieldIntrospection.name;
  201. }, function (fieldIntrospection) {
  202. if (!fieldIntrospection.args) {
  203. throw new Error('Introspection result missing field args: ' + inspect(fieldIntrospection));
  204. }
  205. return {
  206. description: fieldIntrospection.description,
  207. deprecationReason: fieldIntrospection.deprecationReason,
  208. type: getOutputType(fieldIntrospection.type),
  209. args: buildInputValueDefMap(fieldIntrospection.args)
  210. };
  211. });
  212. }
  213. function buildInputValueDefMap(inputValueIntrospections) {
  214. return keyValMap(inputValueIntrospections, function (inputValue) {
  215. return inputValue.name;
  216. }, buildInputValue);
  217. }
  218. function buildInputValue(inputValueIntrospection) {
  219. var type = getInputType(inputValueIntrospection.type);
  220. var defaultValue = inputValueIntrospection.defaultValue ? valueFromAST(parseValue(inputValueIntrospection.defaultValue), type) : undefined;
  221. return {
  222. description: inputValueIntrospection.description,
  223. type: type,
  224. defaultValue: defaultValue
  225. };
  226. }
  227. function buildDirective(directiveIntrospection) {
  228. if (!directiveIntrospection.args) {
  229. throw new Error('Introspection result missing directive args: ' + inspect(directiveIntrospection));
  230. }
  231. if (!directiveIntrospection.locations) {
  232. throw new Error('Introspection result missing directive locations: ' + inspect(directiveIntrospection));
  233. }
  234. return new GraphQLDirective({
  235. name: directiveIntrospection.name,
  236. description: directiveIntrospection.description,
  237. locations: directiveIntrospection.locations.slice(),
  238. args: buildInputValueDefMap(directiveIntrospection.args)
  239. });
  240. }
  241. }