expand.js 10 KB

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