introspection.lua 13 KB

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