123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- local path = (...):gsub('%.[^%.]+$', '')
- local util = require(path .. '.util')
- local types = {}
- function types.nonNull(kind)
- assert(kind, 'Must provide a type')
- return {
- __type = 'NonNull',
- ofType = kind
- }
- end
- function types.list(kind)
- assert(kind, 'Must provide a type')
- return {
- __type = 'List',
- ofType = kind
- }
- end
- function types.scalar(config)
- assert(type(config.name) == 'string', 'type name must be provided as a string')
- assert(type(config.serialize) == 'function', 'serialize must be a function')
- if config.parseValue or config.parseLiteral then
- assert(
- type(config.parseValue) == 'function' and type(config.parseLiteral) == 'function',
- 'must provide both parseValue and parseLiteral to scalar type'
- )
- end
- local instance = {
- __type = 'Scalar',
- name = config.name,
- description = config.description,
- serialize = config.serialize,
- parseValue = config.parseValue,
- parseLiteral = config.parseLiteral
- }
- instance.nonNull = types.nonNull(instance)
- return instance
- end
- function types.object(config)
- assert(type(config.name) == 'string', 'type name must be provided as a string')
- if config.isTypeOf then
- assert(type(config.isTypeOf) == 'function', 'must provide isTypeOf as a function')
- end
- local instance = {
- __type = 'Object',
- name = config.name,
- isTypeOf = config.isTypeOf,
- fields = type(config.fields) == 'function' and util.compose(util.bind1(initFields, 'Object'), config.fields) or initFields('Object', config.fields),
- interfaces = config.interfaces
- }
- instance.nonNull = types.nonNull(instance)
- return instance
- end
- function types.interface(config)
- assert(type(config.name) == 'string', 'type name must be provided as a string')
- assert(type(config.fields) == 'table', 'fields table must be provided')
- if config.resolveType then
- assert(type(config.resolveType) == 'function', 'must provide resolveType as a function')
- end
- local instance = {
- __type = 'Interface',
- name = config.name,
- description = config.description,
- fields = type(config.fields) == 'function' and util.compose(util.bind1(initFields, 'Interface'), config.fields) or initFields('Interface', config.fields),
- resolveType = config.resolveType
- }
- instance.nonNull = types.nonNull(instance)
- return instance
- end
- function initFields(kind, flds)
- assert(type(flds) == 'table', 'fields table must be provided')
- local fields = {}
- for fieldName, field in pairs(flds) do
- field = field.__type and { kind = field } or field
- fields[fieldName] = {
- name = fieldName,
- kind = field.kind,
- arguments = field.arguments or {},
- resolve = kind == 'Object' and field.resolve or nil
- }
- end
- return fields
- end
- function types.enum(config)
- assert(type(config.name) == 'string', 'type name must be provided as a string')
- assert(type(config.values) == 'table', 'values table must be provided')
- local instance = {
- __type = 'Enum',
- name = config.name,
- description = config.description,
- values = config.values
- }
- instance.nonNull = types.nonNull(instance)
- return instance
- end
- function types.union(config)
- assert(type(config.name) == 'string', 'type name must be provided as a string')
- assert(type(config.types) == 'table', 'types table must be provided')
- local instance = {
- __type = 'Union',
- name = config.name,
- types = config.types
- }
- instance.nonNull = types.nonNull(instance)
- return instance
- end
- function types.inputObject(config)
- assert(type(config.name) == 'string', 'type name must be provided as a string')
- local fields = {}
- for fieldName, field in pairs(config.fields) do
- field = field.__type and { kind = field } or field
- fields[fieldName] = {
- name = fieldName,
- kind = field.kind
- }
- end
- local instance = {
- __type = 'InputObject',
- name = config.name,
- description = config.description,
- fields = fields
- }
- return instance
- end
- local coerceInt = function(value)
- value = tonumber(value)
- if not value then return end
- if value == value and value < 2 ^ 32 and value >= -2 ^ 32 then
- return value < 0 and math.ceil(value) or math.floor(value)
- end
- end
- types.int = types.scalar({
- name = 'Int',
- serialize = coerceInt,
- parseValue = coerceInt,
- parseLiteral = function(node)
- if node.kind == 'int' then
- return coerceInt(node.value)
- end
- end
- })
- types.float = types.scalar({
- name = 'Float',
- serialize = tonumber,
- parseValue = tonumber,
- parseLiteral = function(node)
- if node.kind == 'float' or node.kind == 'int' then
- return tonumber(node.value)
- end
- end
- })
- types.string = types.scalar({
- name = 'String',
- serialize = tostring,
- parseValue = tostring,
- parseLiteral = function(node)
- if node.kind == 'string' then
- return node.value
- end
- end
- })
- local function toboolean(x)
- return (x and x ~= 'false') and true or false
- end
- types.boolean = types.scalar({
- name = 'Boolean',
- serialize = toboolean,
- parseValue = toboolean,
- parseLiteral = function(node)
- if node.kind == 'boolean' then
- return toboolean(node.value)
- else
- return nil
- end
- end
- })
- types.id = types.scalar({
- name = 'ID',
- serialize = tostring,
- parseValue = tostring,
- parseLiteral = function(node)
- return node.kind == 'string' or node.kind == 'int' and node.value or nil
- end
- })
- function types.directive(config)
- assert(type(config.name) == 'string', 'type name must be provided as a string')
- local instance = {
- __type = 'Directive',
- name = config.name,
- description = config.description,
- arguments = config.arguments,
- onOperation = config.onOperation or false,
- onFragment = config.onOperation or false,
- onField = config.onField or false
- }
- return instance
- end
- types.include = types.directive({
- name = 'include',
- arguments = {
- ['if'] = types.boolean.nonNull
- },
- onOperation = false,
- onFragment = true,
- onField = true
- })
- types.skip = types.directive({
- name = 'skip',
- arguments = {
- ['if'] = types.boolean.nonNull
- },
- onOperation = false,
- onFragment = true,
- onField = true
- })
- return types
|