123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 |
- 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=2) => {
- if (!allFields && type.fields[0].name !== '_id') allFields++
- if (allFields)
- return '{' +
- type.fields.map(field => {
- return field.name + ((field.type.kind === 'OBJECT' && (inTypes(field.type.name)) && projectionBuilder(inTypes(field.type.name), allFields -1)) ||
- (field.type.kind === 'LIST' && (inTypes(field.type.ofType.name)) && projectionBuilder(inTypes(field.type.ofType.name), allFields -1)) || '')
- })
- .join(',')
- + '}'
- else return `{_id}`
- }
- const identityMap = {}
- let identityMapHits = 0
- let totalObjects = 0
- const createClass = (name, type, input) => {
- if (!(name in classes)) {
- classes[name] = class {
- constructor(data={}, empty = false){
- totalObjects ++
- if (data._id && (data._id in identityMap)){
- if (identityMap[data._id].empty && !empty){
- identityMap[data._id].empty = false
- identityMap[data._id].populate(data)
- }
- identityMapHits++
- console.log(identityMapHits, totalObjects, identityMapHits/totalObjects)
- return identityMap[data._id]
- }
- if (data._id) identityMap[data._id] = this
- this.empty = empty
- this.populate(data)
- }
- 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
- }
- static get inputs(){
- return this.input.inputFields
- }
- 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])
- else data[name] = 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})
- if (result._id && !(result._id in identityMap)){
- identityMap[result._id] = this
- }
- this.populate(result)
- }
- static invalidate(except=[]){
- if (except && except instanceof Array){
- }
- }
- 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;
- }
|