introspection.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. local path = (...):gsub('%.[^%.]+$', '')
  2. local types = require(path .. '.types')
  3. local util = require(path .. '.util')
  4. local __Schema, __Directive, __DirectiveLocation, __Type, __Field, __InputValue,__EnumValue, __TypeKind
  5. __Schema = types.object({
  6. name = '__Schema',
  7. description = util.trim [[
  8. A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types
  9. and directives on the server, as well as the entry points for query and mutation operations.
  10. ]],
  11. fields = function()
  12. return {
  13. types = {
  14. description = 'A list of all types supported by this server.',
  15. kind = types.nonNull(types.list(types.nonNull(__Type))),
  16. resolve = function(schema)
  17. return util.values(schema:getTypeMap())
  18. end
  19. },
  20. queryType = {
  21. description = 'The type that query operations will be rooted at.',
  22. kind = __Type.nonNull,
  23. resolve = function(schema)
  24. return schema:getQueryType()
  25. end
  26. },
  27. mutationType = {
  28. description = 'If this server supports mutation, the type that mutation operations will be rooted at.',
  29. kind = __Type,
  30. resolve = function(schema)
  31. return schema:getMutationType()
  32. end
  33. },
  34. directives = {
  35. description = 'A list of all directives supported by this server.',
  36. kind = types.nonNull(types.list(types.nonNull(__Directive))),
  37. resolve = function(schema)
  38. return schema.directives
  39. end
  40. }
  41. }
  42. end
  43. })
  44. __Directive = types.object({
  45. name = '__Directive',
  46. description = util.trim [[
  47. A Directive provides a way to describe alternate runtime execution and type validation behavior
  48. in a GraphQL document.
  49. In some cases, you need to provide options to alter GraphQL’s execution
  50. behavior in ways field arguments will not suffice, such as conditionally including or skipping a
  51. field. Directives provide this by describing additional information to the executor.
  52. ]],
  53. fields = function()
  54. return {
  55. name = types.nonNull(types.string),
  56. description = types.string,
  57. locations = {
  58. kind = types.nonNull(types.list(types.nonNull(
  59. __DirectiveLocation
  60. ))),
  61. resolve = function(directive)
  62. local res = {}
  63. if directive.onQuery then table.insert(res, 'QUERY') end
  64. if directive.onMutation then table.insert(res, 'MUTATION') end
  65. if directive.onField then table.insert(res, 'FIELD') end
  66. if directive.onFragmentDefinition then table.insert(res, 'FRAGMENT_DEFINITION') end
  67. if directive.onFragmentSpread then table.insert(res, 'FRAGMENT_SPREAD') end
  68. if directive.onInlineFragment then table.insert(res, 'INLINE_FRAGMENT') end
  69. return res
  70. end
  71. },
  72. args = {
  73. kind = types.nonNull(types.list(types.nonNull(__InputValue))),
  74. resolve = function(field)
  75. local args = {}
  76. local transform = function(a, n)
  77. if a.__type then
  78. return { kind = a, name = n }
  79. else
  80. if a.name then return a end
  81. local r = { name = n }
  82. for k,v in pairs(a) do
  83. r[k] = v
  84. end
  85. return r
  86. end
  87. end
  88. for k, v in pairs(field.arguments or {}) do
  89. table.insert(args, transform(v, k))
  90. end
  91. return args
  92. end
  93. }
  94. }
  95. end
  96. })
  97. __DirectiveLocation = types.enum({
  98. name = '__DirectiveLocation',
  99. description = util.trim [[
  100. A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation
  101. describes one such possible adjacencies.
  102. ]],
  103. values = {
  104. QUERY = {
  105. value = 'QUERY',
  106. description = 'Location adjacent to a query operation.'
  107. },
  108. MUTATION = {
  109. value = 'MUTATION',
  110. description = 'Location adjacent to a mutation operation.'
  111. },
  112. FIELD = {
  113. value = 'FIELD',
  114. description = 'Location adjacent to a field.'
  115. },
  116. FRAGMENT_DEFINITION = {
  117. value = 'FRAGMENT_DEFINITION',
  118. description = 'Location adjacent to a fragment definition.'
  119. },
  120. FRAGMENT_SPREAD = {
  121. value = 'FRAGMENT_SPREAD',
  122. description = 'Location adjacent to a fragment spread.'
  123. },
  124. INLINE_FRAGMENT = {
  125. value = 'INLINE_FRAGMENT',
  126. description = 'Location adjacent to an inline fragment.'
  127. }
  128. }
  129. })
  130. __Type = types.object({
  131. name = '__Type',
  132. description = util.trim [[
  133. The fundamental unit of any GraphQL Schema is the type. There are
  134. many kinds of types in GraphQL as represented by the `__TypeKind` enum.
  135. Depending on the kind of a type, certain fields describe
  136. information about that type. Scalar types provide no information
  137. beyond a name and description, while Enum types provide their values.
  138. Object and Interface types provide the fields they describe. Abstract
  139. types, Union and Interface, provide the Object types possible
  140. at runtime. List and NonNull types compose other types.
  141. ]],
  142. fields = function()
  143. return {
  144. name = types.string,
  145. description = types.string,
  146. kind = {
  147. kind = __TypeKind.nonNull,
  148. resolve = function(kind)
  149. if kind.__type == 'Scalar' then
  150. return 'SCALAR'
  151. elseif kind.__type == 'Object' then
  152. return 'OBJECT'
  153. elseif kind.__type == 'Interface' then
  154. return 'INTERFACE'
  155. elseif kind.__type == 'Union' then
  156. return 'UNION'
  157. elseif kind.__type == 'Enum' then
  158. return 'ENUM'
  159. elseif kind.__type == 'InputObject' then
  160. return 'INPUT_OBJECT'
  161. elseif kind.__type == 'List' then
  162. return 'LIST'
  163. elseif kind.__type == 'NonNull' then
  164. return 'NON_NULL'
  165. end
  166. error('Unknown type ' .. kind)
  167. end
  168. },
  169. fields = {
  170. kind = types.list(types.nonNull(__Field)),
  171. arguments = {
  172. includeDeprecated = {
  173. kind = types.boolean,
  174. defaultValue = false
  175. }
  176. },
  177. resolve = function(kind, arguments)
  178. if kind.__type == 'Object' or kind.__type == 'Interface' then
  179. return util.filter(util.values(kind.fields), function(field)
  180. return arguments.includeDeprecated or field.deprecationReason == nil
  181. end)
  182. end
  183. return nil
  184. end
  185. },
  186. interfaces = {
  187. kind = types.list(types.nonNull(__Type)),
  188. resolve = function(kind)
  189. if kind.__type == 'Object' then
  190. return kind.interfaces
  191. end
  192. end
  193. },
  194. possibleTypes = {
  195. kind = types.list(types.nonNull(__Type)),
  196. resolve = function(kind, arguments, context)
  197. if kind.__type == 'Interface' or kind.__type == 'Union' then
  198. return context.schema:getPossibleTypes(kind)
  199. end
  200. end
  201. },
  202. enumValues = {
  203. kind = types.list(types.nonNull(__EnumValue)),
  204. arguments = {
  205. includeDeprecated = { kind = types.boolean, defaultValue = false }
  206. },
  207. resolve = function(kind, arguments)
  208. if kind.__type == 'Enum' then
  209. return util.filter(util.values(kind.values), function(value)
  210. return arguments.includeDeprecated or not value.deprecationReason
  211. end)
  212. end
  213. end
  214. },
  215. inputFields = {
  216. kind = types.list(types.nonNull(__InputValue)),
  217. resolve = function(kind)
  218. if kind.__type == 'InputObject' then
  219. return util.values(kind.fields)
  220. end
  221. end
  222. },
  223. ofType = {
  224. kind = __Type
  225. }
  226. }
  227. end
  228. })
  229. __Field = types.object({
  230. name = '__Field',
  231. description = util.trim [[
  232. Object and Interface types are described by a list of Fields, each of
  233. which has a name, potentially a list of arguments, and a return type.
  234. ]],
  235. fields = function()
  236. return {
  237. name = types.string.nonNull,
  238. description = types.string,
  239. args = {
  240. -- kind = types.list(__InputValue),
  241. kind = types.nonNull(types.list(types.nonNull(__InputValue))),
  242. resolve = function(field)
  243. return util.map(field.arguments or {}, function(a, n)
  244. if a.__type then
  245. return { kind = a, name = n }
  246. else
  247. if not a.name then
  248. local r = { name = n }
  249. for k,v in pairs(a) do
  250. r[k] = v
  251. end
  252. return r
  253. else
  254. return a
  255. end
  256. end
  257. end)
  258. end
  259. },
  260. type = {
  261. kind = __Type.nonNull,
  262. resolve = function(field)
  263. return field.kind
  264. end
  265. },
  266. isDeprecated = {
  267. kind = types.boolean.nonNull,
  268. resolve = function(field)
  269. return field.deprecationReason ~= nil
  270. end
  271. },
  272. deprecationReason = types.string
  273. }
  274. end
  275. })
  276. __InputValue = types.object({
  277. name = '__InputValue',
  278. description = util.trim [[
  279. Arguments provided to Fields or Directives and the input fields of an
  280. InputObject are represented as Input Values which describe their type
  281. and optionally a default value.
  282. ]],
  283. fields = function()
  284. return {
  285. name = types.string.nonNull,
  286. description = types.string,
  287. type = {
  288. kind = types.nonNull(__Type),
  289. resolve = function(field)
  290. return field.kind
  291. end
  292. },
  293. defaultValue = {
  294. kind = types.string,
  295. description = 'A GraphQL-formatted string representing the default value for this input value.',
  296. resolve = function(inputVal)
  297. return inputVal.defaultValue and tostring(inputVal.defaultValue) -- TODO improve serialization a lot
  298. end
  299. }
  300. }
  301. end
  302. })
  303. __EnumValue = types.object({
  304. name = '__EnumValue',
  305. description = [[
  306. One possible value for a given Enum. Enum values are unique values, not
  307. a placeholder for a string or numeric value. However an Enum value is
  308. returned in a JSON response as a string.
  309. ]],
  310. fields = function()
  311. return {
  312. name = types.string.nonNull,
  313. description = types.string,
  314. isDeprecated = {
  315. kind = types.boolean.nonNull,
  316. resolve = function(enumValue) return enumValue.deprecationReason ~= nil end
  317. },
  318. deprecationReason = types.string
  319. }
  320. end
  321. })
  322. __TypeKind = types.enum({
  323. name = '__TypeKind',
  324. description = 'An enum describing what kind of type a given `__Type` is.',
  325. values = {
  326. SCALAR = {
  327. value = 'SCALAR',
  328. description = 'Indicates this type is a scalar.'
  329. },
  330. OBJECT = {
  331. value = 'OBJECT',
  332. description = 'Indicates this type is an object. `fields` and `interfaces` are valid fields.'
  333. },
  334. INTERFACE = {
  335. value = 'INTERFACE',
  336. description = 'Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.'
  337. },
  338. UNION = {
  339. value = 'UNION',
  340. description = 'Indicates this type is a union. `possibleTypes` is a valid field.'
  341. },
  342. ENUM = {
  343. value = 'ENUM',
  344. description = 'Indicates this type is an enum. `enumValues` is a valid field.'
  345. },
  346. INPUT_OBJECT = {
  347. value = 'INPUT_OBJECT',
  348. description = 'Indicates this type is an input object. `inputFields` is a valid field.'
  349. },
  350. LIST = {
  351. value = 'LIST',
  352. description = 'Indicates this type is a list. `ofType` is a valid field.'
  353. },
  354. NON_NULL = {
  355. value = 'NON_NULL',
  356. description = 'Indicates this type is a non-null. `ofType` is a valid field.'
  357. }
  358. }
  359. })
  360. local Schema = {
  361. name = '__schema',
  362. kind = __Schema.nonNull,
  363. description = 'Access the current type schema of this server.',
  364. arguments = {},
  365. resolve = function(_, _, info)
  366. return info.schema
  367. end
  368. }
  369. local Type = {
  370. name = '__type',
  371. kind = __Type,
  372. description = 'Request the type information of a single type.',
  373. arguments = {
  374. name = types.string.nonNull
  375. },
  376. resolve = function(_, arguments, info)
  377. return info.schema:getType(arguments.name)
  378. end
  379. }
  380. local TypeName = {
  381. name = '__typename',
  382. kind = types.string.nonNull,
  383. description = 'The name of the current Object type at runtime.',
  384. arguments = {},
  385. resolve = function(_, _, info)
  386. return info.parentType.name
  387. end
  388. }
  389. return {
  390. __Schema = __Schema,
  391. __Directive = __Directive,
  392. __DirectiveLocation = __DirectiveLocation,
  393. __Type = __Type,
  394. __Field = __Field,
  395. __EnumValue = __EnumValue,
  396. __TypeKind = __TypeKind,
  397. Schema = Schema,
  398. Type = Type,
  399. TypeName = TypeName,
  400. fieldMap = {
  401. __schema = Schema,
  402. __type = Type,
  403. __typename = TypeName
  404. }
  405. }