index.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. const ObjectID = require("mongodb").ObjectID;
  2. const jwt = require('jsonwebtoken')
  3. const jwtSecret = 'CbymrfGfnB'
  4. const express = require('express');
  5. const express_graphql = require('express-graphql');
  6. const { buildSchema, GraphQLObjectType, GraphQLString, GraphQLList, GraphQLSchema, printSchema } = require('graphql');
  7. function mmExpandSchema(gqlSchema){
  8. const types = {}
  9. const _typeMap = gqlSchema.getTypeMap()
  10. const buildInTypes = ['Query', 'Mutation', 'ID', 'Float', "String", 'Int', 'Boolean',
  11. 'Query!', 'Mutation!', 'ID!', 'Float!', "String!", 'Int!', 'Boolean!' ]
  12. async function argToSavables(arg, outputTypeName, Savable){
  13. console.log('argToSavables', arg)
  14. const entity = arg._id ? await Savable.m[outputTypeName].findOne({_id: ObjectID(arg._id)}) :
  15. new Savable.classes[outputTypeName]({})
  16. const {_id, ...data} = arg;
  17. const type = _typeMap[outputTypeName + 'Input']
  18. const fields = type.getFields()
  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. if (typeName[0] === '['){
  24. const nestedTypeName = typeName.slice(1,-6)
  25. console.log('array',nestedTypeName)
  26. entity[fieldName] = []
  27. for (let nestedArg of value){
  28. const nestedEntity = await argToSavables(nestedArg, nestedTypeName, Savable)
  29. entity[fieldName].push(nestedEntity)
  30. }
  31. }
  32. else {
  33. const nestedTypeName = typeName.slice(0,-5)
  34. console.log('one', nestedTypeName)
  35. entity[fieldName] = await argToSavables(value, nestedTypeName, Savable)
  36. }
  37. }
  38. else {
  39. entity[fieldName] = value
  40. }
  41. }
  42. return await entity.save()
  43. }
  44. let queryFields = _typeMap.Query ? _typeMap.Query.getFields() : {}
  45. let mutationFields = _typeMap.Mutation ? _typeMap.Mutation.getFields() : {}
  46. for (let [typeName, type] of Object.entries(_typeMap))
  47. if (!buildInTypes.includes(typeName) &&
  48. !typeName.startsWith('__')){
  49. if (typeName.endsWith('Input')){
  50. let outputTypeName = typeName.substr(0, typeName.length - 'Input'.length)
  51. if (outputTypeName in _typeMap){
  52. types[outputTypeName] = type
  53. const find = {
  54. type: GraphQLList(_typeMap[outputTypeName]),
  55. args: {query: {type: GraphQLString}},
  56. async resolve(root, args, context, info){
  57. //console.log(root, args, context, info)
  58. const Savable = context.models.SlicedSavable || context.models.Savable
  59. args = JSON.parse(args.query)
  60. let results = []
  61. for (let result of Savable.m[outputTypeName].find(...args)){
  62. try {result = await result} catch (e) { break }
  63. results.push(result)
  64. }
  65. return results;
  66. }
  67. }
  68. queryFields[`${outputTypeName}Find`] = find
  69. const findOne = {
  70. type: _typeMap[outputTypeName],
  71. args: {query: {type: GraphQLString}},
  72. async resolve(root, args, context, info){
  73. //console.log(root, args, context, info)
  74. const Savable = context.models.SlicedSavable || context.models.Savable
  75. args = JSON.parse(args.query)
  76. let [query] = args
  77. if (query._id && typeof query._id === 'string'){
  78. query._id = ObjectID(query._id)
  79. }
  80. let record = Savable.m[outputTypeName].findOne(query, ...args.slice(1))
  81. return record;
  82. }
  83. }
  84. queryFields[`${outputTypeName}FindOne`] = findOne
  85. const lowerCaseName = outputTypeName[0].toLowerCase() + outputTypeName.slice(1)
  86. const del = {
  87. type: _typeMap[outputTypeName],
  88. args: {[lowerCaseName]: {type: _typeMap[typeName]}},
  89. async resolve(root, args, context, info){
  90. //console.log(root, args, context, info)
  91. const Savable = context.models.SlicedSavable || context.models.Savable
  92. const arg = args[lowerCaseName]
  93. if (! ('_id' in arg)){
  94. return null;
  95. }
  96. let entity = await Savable.m[outputTypeName].findOne({_id: ObjectID(arg._id)})
  97. if (entity){
  98. let copy = {...record}
  99. await entity.delete()
  100. return copy;
  101. }
  102. return entity;
  103. }
  104. }
  105. mutationFields[`${outputTypeName}Delete`] = del
  106. const upsert = {
  107. type: _typeMap[outputTypeName],
  108. args: {[lowerCaseName]: {type: _typeMap[typeName]}},
  109. async resolve(root, args, context, info){
  110. //console.log(root, args, context, info)
  111. const Savable = context.models.SlicedSavable || context.models.Savable
  112. const arg = args[lowerCaseName]
  113. const entity = argToSavables(args[lowerCaseName], outputTypeName, Savable)
  114. return entity;
  115. }
  116. }
  117. mutationFields[`${outputTypeName}Upsert`] = upsert
  118. }
  119. }
  120. }
  121. let newQuery = new GraphQLObjectType({name: 'Query', fields: queryFields})
  122. let newMutation = new GraphQLObjectType({name: 'Mutation', fields: mutationFields})
  123. let newSchema = new GraphQLSchema({query: newQuery, mutation: newMutation})
  124. return newSchema;
  125. }
  126. ;(async () => {
  127. const {Savable, slice, getModels} = await require('./models.js')()
  128. const jwtGQL = require('./jwt')
  129. class User extends Savable {
  130. static get relations(){
  131. return {
  132. }
  133. }
  134. }
  135. Savable.addClass(User)
  136. let schema = buildSchema(`
  137. type User {
  138. _id: String
  139. createdAt: String
  140. login: String
  141. nick : String
  142. orders: [Order]
  143. }
  144. input UserInput {
  145. _id: String
  146. login: String
  147. nick : String
  148. }
  149. type Category {
  150. _id: ID,
  151. createdAt: String
  152. name: String!,
  153. goods: [Good]
  154. }
  155. input CategoryInput {
  156. _id: ID,
  157. name: String,
  158. goods: [GoodInput]
  159. }
  160. type Good {
  161. _id: ID,
  162. createdAt: String
  163. name: String!,
  164. description: String
  165. price: Float
  166. imgUrls: [String]
  167. orderGoods: [OrderGood]
  168. categories: [Category]
  169. }
  170. input GoodInput {
  171. _id: ID,
  172. name: String,
  173. description: String
  174. imgUrls: [String]
  175. price: Float
  176. categories: [CategoryInput]
  177. }
  178. type OrderGood {
  179. _id: ID,
  180. createdAt: String
  181. price: Float,
  182. count: Float,
  183. good: Good,
  184. order: Order
  185. total: Float
  186. }
  187. input OrderGoodInput {
  188. _id: ID,
  189. count: Int,
  190. good: GoodInput,
  191. order: OrderInput
  192. }
  193. type Order {
  194. _id: ID
  195. createdAt: String
  196. orderGoods: [OrderGood]
  197. total: Float
  198. }
  199. input OrderInput {
  200. _id: ID
  201. orderGoods: [OrderGoodInput]
  202. }
  203. `);
  204. schema = mmExpandSchema(schema)
  205. console.log(printSchema(schema))
  206. var app = express();
  207. app.use(express.static('public'));
  208. const anonResolvers = {
  209. createUser:async function ({login, password}){
  210. let user = await Savable.m.User.findOne({login, password})
  211. if (user)
  212. return null;
  213. user = await (new User({login, password})).save()
  214. user.___owner = user._id.toString()
  215. user.___permissions = {
  216. read: ["owner", "user"]
  217. }
  218. return await user.save()
  219. },
  220. login: async function({login, password}){
  221. console.log(Savable.classes)
  222. const user = await Savable.m.User.findOne({login, password})
  223. if (!user)
  224. return null;
  225. const token = jwt.sign({ sub: {id: user._id, login}}, jwtSecret); //подписывам токен нашим ключем
  226. return token
  227. },
  228. changePassword:async function ({login, password, newPassword}){
  229. const user = await Savable.m.User.findOne({login, password})
  230. if (!user) return null;
  231. user.password = newPassword;
  232. return await user.save()
  233. },
  234. }
  235. const anonSchema = buildSchema(`
  236. type Query {
  237. login(login: String!, password: String!): String
  238. }
  239. type Mutation {
  240. createUser(login: String!, password: String!): User
  241. changePassword(login: String!, password: String!, newPassword: String!): User
  242. }
  243. type User {
  244. _id: String
  245. createdAt: String
  246. login: String
  247. nick : String
  248. }
  249. `)
  250. app.use('/graphql', express_graphql(jwtGQL({anonSchema, anonResolvers, schema, createContext: getModels, graphiql: true, secret: jwtSecret})))
  251. app.listen(4000, () => console.log('Express GraphQL Server Now Running On localhost:4000/graphql'));
  252. })()