|
@@ -0,0 +1,249 @@
|
|
|
+
|
|
|
+export default async function createModels2(gql, config={create: 'Upsert', update: 'Upsert', delete: "Delete", findOne: "FindOne", find: 'Find', count: 'Count'}){
|
|
|
+ const universeQuery = `query universe{
|
|
|
+ __schema{
|
|
|
+ types {
|
|
|
+ name,
|
|
|
+ kind,
|
|
|
+ inputFields{
|
|
|
+ name,
|
|
|
+ type {
|
|
|
+ kind,
|
|
|
+ ofType{
|
|
|
+ name,
|
|
|
+ fields{
|
|
|
+ name,
|
|
|
+ type{
|
|
|
+ name,
|
|
|
+ kind
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ name,
|
|
|
+ fields{
|
|
|
+ name,
|
|
|
+ type{
|
|
|
+ name,
|
|
|
+ kind
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ fields {
|
|
|
+ name,
|
|
|
+ type {
|
|
|
+ kind,
|
|
|
+ ofType{
|
|
|
+ name,
|
|
|
+ fields{
|
|
|
+ name,
|
|
|
+ type{
|
|
|
+ name,
|
|
|
+ kind
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ name,
|
|
|
+ fields{
|
|
|
+ name,
|
|
|
+ type{
|
|
|
+ name,
|
|
|
+ kind
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ args{
|
|
|
+ name,
|
|
|
+ type{
|
|
|
+ name,
|
|
|
+ inputFields{
|
|
|
+ name,
|
|
|
+ type{
|
|
|
+ name,
|
|
|
+ kind,
|
|
|
+ ofType{
|
|
|
+ name
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ `
|
|
|
+
|
|
|
+ const universe = await gql.request(universeQuery)
|
|
|
+ console.log(universe)
|
|
|
+ const types = []
|
|
|
+ const inputs = []
|
|
|
+ for (let type of universe.__schema.types){
|
|
|
+ if (!["Query", "Mutation"].includes(type.name) && type.kind === "OBJECT" && !type.name.startsWith('__')) types.push(type)
|
|
|
+ if (!["Query", "Mutation"].includes(type.name) && type.kind === "INPUT_OBJECT" && !type.name.startsWith('__')) inputs.push(type)
|
|
|
+ }
|
|
|
+ console.log(types, inputs)
|
|
|
+
|
|
|
+ let classes = {}
|
|
|
+
|
|
|
+ const inTypes = name => types.find(type => type.name === name)
|
|
|
+ const inInputs = name => inputs.find(type => type.name === name)
|
|
|
+
|
|
|
+ const projectionBuilder = (type, allFields=true) => {
|
|
|
+ if (!allFields && type.fields[0].name !== '_id') allFields = true
|
|
|
+ if (allFields)
|
|
|
+ return '{' +
|
|
|
+ type.fields.map(field => {
|
|
|
+ return field.name + ((field.type.kind === 'OBJECT' && (inTypes(field.type.name)) && projectionBuilder(inTypes(field.type.name), false)) ||
|
|
|
+ (field.type.kind === 'LIST' && (inTypes(field.type.ofType.name)) && projectionBuilder(inTypes(field.type.ofType.name), false)) || '')
|
|
|
+ })
|
|
|
+ .join(',')
|
|
|
+ + '}'
|
|
|
+ else return `{_id}`
|
|
|
+ }
|
|
|
+
|
|
|
+ const identityMap = {}
|
|
|
+
|
|
|
+
|
|
|
+ const createClass = (name, type, input) => {
|
|
|
+ if (!(name in classes)) {
|
|
|
+ classes[name] = class {
|
|
|
+ constructor(data={}, empty = false){
|
|
|
+ if (data._id && data._id in identityMap)
|
|
|
+ return identityMap[data._id]
|
|
|
+
|
|
|
+ this.populate(data)
|
|
|
+
|
|
|
+ this.empty = empty
|
|
|
+
|
|
|
+ if (this._id) identityMap[this._id] = this
|
|
|
+ }
|
|
|
+
|
|
|
+ populate(data){
|
|
|
+ type.fields.forEach(({name, type:{ ofType, kind, name:otherName}}) => {
|
|
|
+ ({SCALAR(){
|
|
|
+ if (data && typeof data === 'object' && name in data) this[name] = data[name]
|
|
|
+ },
|
|
|
+ LIST(){
|
|
|
+ const otherType = inTypes(ofType.name)
|
|
|
+ if (otherType && data[name])
|
|
|
+ this[name] = data[name].map(otherEntity => new classes[otherType.name](otherEntity, otherEntity._id && Object.keys(otherEntity).length === 1))
|
|
|
+ else if (data && typeof data === 'object' && name in data) this[name] = data[name]
|
|
|
+ },
|
|
|
+ OBJECT(){
|
|
|
+ const otherType = inTypes(otherName)
|
|
|
+ if (otherType && data[name])
|
|
|
+ this[name] = new classes[otherType.name](data[name], data[name]._id && Object.keys(data[name]).length === 1)
|
|
|
+ else if (data && typeof data === 'object' && name in data) this[name] = data[name]
|
|
|
+ }})[kind].call(this)
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ get empty(){
|
|
|
+ return this.then
|
|
|
+ }
|
|
|
+
|
|
|
+ set empty(value){
|
|
|
+ if (value){
|
|
|
+ this.then = async (onFullfilled, onRejected) => {
|
|
|
+ const gqlQuery = `
|
|
|
+ query ${name}FindOne($query: String){
|
|
|
+ ${name}FindOne(query: $query)
|
|
|
+ ${projectionBuilder(type)}
|
|
|
+
|
|
|
+ }
|
|
|
+ `
|
|
|
+ const data = await gql.request(gqlQuery, {query: JSON.stringify([{_id: this._id}])})
|
|
|
+ this.populate(data[name + 'FindOne'])
|
|
|
+ this.empty = false
|
|
|
+ const thenResult = onFullfilled(this)
|
|
|
+ if (thenResult && typeof thenResult === 'object' && typeof thenResult.then === 'function'){
|
|
|
+ return await thenResult
|
|
|
+ }
|
|
|
+ return thenResult
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else delete this.then
|
|
|
+ }
|
|
|
+
|
|
|
+ static get type(){
|
|
|
+ return type
|
|
|
+ }
|
|
|
+
|
|
|
+ static get input(){
|
|
|
+ return input
|
|
|
+ }
|
|
|
+
|
|
|
+ static get fields(){
|
|
|
+ return this.type.fields
|
|
|
+ }
|
|
|
+
|
|
|
+ async save(){
|
|
|
+ if (this.empty) throw new ReferenceError('Cannot save empty object')
|
|
|
+ const data = {}
|
|
|
+ input.inputFields.forEach(({name, type:{ ofType, kind, name:otherName}}) => {
|
|
|
+ ({SCALAR(){
|
|
|
+ if (this[name]) data[name] = this[name]
|
|
|
+ },
|
|
|
+ LIST(){
|
|
|
+ const otherType = inInputs(ofType.name)
|
|
|
+ if (otherType && this[name] && this[name] instanceof Array)
|
|
|
+ data[name] = this[name].map(otherEntity => (otherEntity._id ? {_id: otherEntity._id} : otherEntity))
|
|
|
+ },
|
|
|
+ INPUT_OBJECT(){
|
|
|
+ const otherType = inInputs(otherName)
|
|
|
+ if (otherType && this[name] && typeof this[name] === 'object')
|
|
|
+ data[name] = (this[name]._id ? {_id: this[name]._id} : this[name])
|
|
|
+ }})[kind].call(this)
|
|
|
+ })
|
|
|
+ const gqlQuery = `
|
|
|
+ mutation ${name}Upsert($data: ${input.name}){
|
|
|
+ ${name}Upsert(${name.toLowerCase()}: $data)
|
|
|
+ ${projectionBuilder(type)}
|
|
|
+ }
|
|
|
+ `
|
|
|
+ let result = await gql.request(gqlQuery, {data})
|
|
|
+ this.populate(result)
|
|
|
+ }
|
|
|
+
|
|
|
+ static async find(query={}, cursorCalls={}){
|
|
|
+ const gqlQuery = `
|
|
|
+ query ${name}Find($query: String){
|
|
|
+ ${name}Find(query: $query)
|
|
|
+ ${projectionBuilder(type)}
|
|
|
+ }
|
|
|
+ `
|
|
|
+ let result = await gql.request(gqlQuery, {query: JSON.stringify([query, cursorCalls])})
|
|
|
+ return result[name + 'Find'].map(entity => new classes[name](entity))
|
|
|
+ }
|
|
|
+
|
|
|
+ static async findOne(query){
|
|
|
+ const gqlQuery = `
|
|
|
+ query ${name}FindOne($query: String){
|
|
|
+ ${name}FindOne(query: $query)
|
|
|
+ ${projectionBuilder(type)}
|
|
|
+ }
|
|
|
+ `
|
|
|
+ let result = await gql.request(gqlQuery, {query: JSON.stringify([query])})
|
|
|
+ return result[name + 'FindOne']
|
|
|
+ }
|
|
|
+
|
|
|
+ static async count(query){
|
|
|
+ const gqlQuery = `
|
|
|
+ query ${name}Count($query: String){
|
|
|
+ ${name}Count(query: $query)
|
|
|
+ }
|
|
|
+ `
|
|
|
+ let result = await gql.request(gqlQuery, {query: JSON.stringify([query])})
|
|
|
+ return result[name + 'Count']
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Object.defineProperty(classes[name], 'name', {value: name})
|
|
|
+ }
|
|
|
+ return classes[name]
|
|
|
+ }
|
|
|
+
|
|
|
+ types.forEach((type) => createClass(type.name, type, inputs.find(input => input.name === `${type.name}Input`)))
|
|
|
+ return classes;
|
|
|
+}
|