expand.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. const { buildSchema, GraphQLObjectType, GraphQLString, GraphQLInt, GraphQLList, GraphQLSchema } = require('graphql');
  2. const ObjectID = require("mongodb").ObjectID;
  3. function mmExpandSchema(gqlSchema, defaultQueryFields, defaultMutationFields){
  4. const types = {}
  5. const _typeMap = gqlSchema.getTypeMap()
  6. const buildInTypes = ['Query', 'Mutation', 'ID', 'Float', "String", 'Int', 'Boolean',
  7. 'Query!', 'Mutation!', 'ID!', 'Float!', "String!", 'Int!', 'Boolean!',
  8. '[Query!]', '[Mutation!]', '[ID!]', '[Float!]', "[String!]", '[Int!]', '[Boolean!]',
  9. '[Query]', '[Mutation]', '[ID]', '[Float]', "[String]", '[Int]', '[Boolean]',
  10. ]
  11. async function argToSavables(arg, outputTypeName, Savable){
  12. console.log('argToSavables', arg)
  13. const entity = arg._id ? await Savable.m[outputTypeName].findOne({_id: ObjectID(arg._id)}) :
  14. new Savable.classes[outputTypeName]({})
  15. const {_id, ...data} = arg;
  16. const type = _typeMap[outputTypeName + 'Input']
  17. const fields = type.getFields()
  18. let changed = !_id
  19. for(let [fieldName, value] of Object.entries(data)){
  20. let typeName = fields[fieldName].type.toString()
  21. if (!buildInTypes.includes(typeName)){
  22. console.log('recursive', arg[fieldName], typeName)
  23. changed = true
  24. if (typeName[0] === '['){
  25. const nestedTypeName = typeName.slice(1,-6)
  26. console.log('array',nestedTypeName)
  27. entity[fieldName] = []
  28. if (value) for (let nestedArg of value){
  29. const nestedEntity = await argToSavables(nestedArg, nestedTypeName, Savable)
  30. entity[fieldName].push(nestedEntity)
  31. }
  32. }
  33. else {
  34. const nestedTypeName = typeName.slice(0,-5)
  35. console.log('one', nestedTypeName)
  36. entity[fieldName] = await argToSavables(value, nestedTypeName, Savable)
  37. }
  38. }
  39. else {
  40. entity[fieldName] = value
  41. }
  42. }
  43. console.log('changed', changed)
  44. changed && await entity.save()
  45. return entity
  46. }
  47. let queryFields = _typeMap.Query ? _typeMap.Query.getFields() : {}
  48. let mutationFields = _typeMap.Mutation ? _typeMap.Mutation.getFields() : {}
  49. for (let [typeName, type] of Object.entries(_typeMap))
  50. if (!buildInTypes.includes(typeName) &&
  51. !typeName.startsWith('__')){
  52. if (typeName.endsWith('Input')){
  53. let outputTypeName = typeName.substr(0, typeName.length - 'Input'.length)
  54. if (outputTypeName in _typeMap){
  55. types[outputTypeName] = type
  56. const queryUpdater = query => {
  57. const checkers = [
  58. function objectID(val){
  59. if (val && typeof val === 'string' && val.length == 24){
  60. try {
  61. const id = ObjectID(val)
  62. if (id.toString() === val) return id
  63. }
  64. catch (e){
  65. return val
  66. }
  67. }
  68. return val
  69. },
  70. function regexp(val){
  71. if (val && typeof val === 'string' && val.startsWith('/') && val.endsWith('/')){
  72. console.log('regexp found' ,val )
  73. try {
  74. return new RegExp(val.slice(1, -1))
  75. }
  76. catch (e){
  77. return val
  78. }
  79. }
  80. return val
  81. },
  82. ]
  83. const checker = val => {
  84. const originalVal = val
  85. for (let lambda of checkers){
  86. val = lambda(val)
  87. }
  88. return val !== originalVal && val
  89. }
  90. const walker = obj =>{
  91. for (let [key, value] of Object.entries(obj)){
  92. if (key === '___owner') continue;
  93. let newValue;
  94. if (newValue = checker(value)) obj[key] = newValue;
  95. else if (typeof value === 'object'){
  96. obj[key] = walker(value)
  97. }
  98. }
  99. return obj
  100. }
  101. return walker(query)
  102. }
  103. const find = {
  104. type: GraphQLList(_typeMap[outputTypeName]),
  105. args: {query: {type: GraphQLString}},
  106. async resolve(root, args, context, info){
  107. //console.log(root, args, context, info)
  108. const Savable = context.models.SlicedSavable || context.models.Savable
  109. args = JSON.parse(args.query)
  110. queryUpdater(args[0])
  111. //console.log(args)
  112. let results = []
  113. for (let result of Savable.m[outputTypeName].find(...args)){
  114. try {result = await result} catch (e) { break }
  115. results.push(result)
  116. }
  117. return results;
  118. }
  119. }
  120. queryFields[`${outputTypeName}Find`] = find
  121. const count = {
  122. type: GraphQLInt,
  123. args: {query: {type: GraphQLString}},
  124. async resolve(root, args, context, info){
  125. const Savable = context.models.SlicedSavable || context.models.Savable
  126. args = JSON.parse(args.query)
  127. queryUpdater(args[0])
  128. return await Savable.m[outputTypeName].count(...args)
  129. }
  130. }
  131. queryFields[`${outputTypeName}Count`] = count
  132. const findOne = {
  133. type: _typeMap[outputTypeName],
  134. args: {query: {type: GraphQLString}},
  135. async resolve(root, args, context, info){
  136. //console.log(root, args, context, info)
  137. const Savable = context.models.SlicedSavable || context.models.Savable
  138. args = JSON.parse(args.query)
  139. let [query] = args
  140. queryUpdater(query)
  141. console.log(query)
  142. let record = Savable.m[outputTypeName].findOne(query, ...args.slice(1))
  143. return record;
  144. }
  145. }
  146. queryFields[`${outputTypeName}FindOne`] = findOne
  147. const lowerCaseName = outputTypeName[0].toLowerCase() + outputTypeName.slice(1)
  148. const del = {
  149. type: _typeMap[outputTypeName],
  150. args: {[lowerCaseName]: {type: _typeMap[typeName]}},
  151. async resolve(root, args, context, info){
  152. //console.log(root, args, context, info)
  153. const Savable = context.models.SlicedSavable || context.models.Savable
  154. const arg = args[lowerCaseName]
  155. if (! ('_id' in arg)){
  156. return null;
  157. }
  158. let entity = await Savable.m[outputTypeName].findOne({_id: ObjectID(arg._id)})
  159. if (entity){
  160. let copy = {...entity}
  161. await entity.delete()
  162. return copy;
  163. }
  164. return entity;
  165. }
  166. }
  167. mutationFields[`${outputTypeName}Delete`] = del
  168. const upsert = {
  169. type: _typeMap[outputTypeName],
  170. args: {[lowerCaseName]: {type: _typeMap[typeName]}},
  171. async resolve(root, args, context, info){
  172. //console.log(root, args, context, info)
  173. const Savable = context.models.SlicedSavable || context.models.Savable
  174. const arg = args[lowerCaseName]
  175. const entity = argToSavables(args[lowerCaseName], outputTypeName, Savable)
  176. return entity;
  177. }
  178. }
  179. mutationFields[`${outputTypeName}Upsert`] = upsert
  180. }
  181. }
  182. }
  183. let newQuery = new GraphQLObjectType({name: 'Query', fields: {...defaultQueryFields, ...queryFields}})
  184. let newMutation = new GraphQLObjectType({name: 'Mutation', fields: {...defaultMutationFields, ...mutationFields}})
  185. let newSchema = new GraphQLSchema({query: newQuery, mutation: newMutation})
  186. return newSchema;
  187. }
  188. module.exports = mmExpandSchema