expand.js 9.6 KB

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