mirror of https://github.com/nocodb/nocodb
Wing-Kam Wong
2 years ago
11 changed files with 0 additions and 75972 deletions
File diff suppressed because it is too large
Load Diff
@ -1,37 +0,0 @@ |
|||||||
// refer : https://stackoverflow.com/a/6713782
|
|
||||||
|
|
||||||
module.exports = function compareObj(x, y) { |
|
||||||
if (x === y) return true; |
|
||||||
// if both x and y are null or undefined and exactly the same
|
|
||||||
|
|
||||||
if (!(x instanceof Object) || !(y instanceof Object)) return false; |
|
||||||
// if they are not strictly equal, they both need to be Objects
|
|
||||||
|
|
||||||
if (x.constructor !== y.constructor) return false; |
|
||||||
// they must have the exact same prototype chain, the closest we can do is
|
|
||||||
// test there constructor.
|
|
||||||
|
|
||||||
for (var p in x) { |
|
||||||
if (!x.hasOwnProperty(p)) continue; |
|
||||||
// other properties were tested using x.constructor === y.constructor
|
|
||||||
|
|
||||||
if (!y.hasOwnProperty(p)) return false; |
|
||||||
// allows to compare x[ p ] and y[ p ] when set to undefined
|
|
||||||
|
|
||||||
if (x[p] === y[p]) continue; |
|
||||||
// if they have the same strict value or identity then they are equal
|
|
||||||
|
|
||||||
if (typeof (x[p]) !== "object") return false; |
|
||||||
// Numbers, Strings, Functions, Booleans must be strictly equal
|
|
||||||
|
|
||||||
if (!compareObj(x[p], y[p])) return false; |
|
||||||
// Objects and Arrays must be tested recursively
|
|
||||||
} |
|
||||||
|
|
||||||
for (p in y) |
|
||||||
if (y.hasOwnProperty(p) && !x.hasOwnProperty(p)) |
|
||||||
return false; |
|
||||||
// allows x[ p ] to be set to undefined
|
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
@ -1,32 +0,0 @@ |
|||||||
const { |
|
||||||
graphql, |
|
||||||
GraphQLSchema, |
|
||||||
GraphQLObjectType, |
|
||||||
GraphQLString, |
|
||||||
} = require( 'graphql'); |
|
||||||
|
|
||||||
var schema = new GraphQLSchema({ |
|
||||||
query: new GraphQLObjectType({ |
|
||||||
name: 'RootQueryType', |
|
||||||
fields: { |
|
||||||
hello: { |
|
||||||
type: GraphQLString, |
|
||||||
resolve() { |
|
||||||
return 'world'; |
|
||||||
}, |
|
||||||
}, |
|
||||||
}, |
|
||||||
}), |
|
||||||
}); |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var source = '{ hello }'; |
|
||||||
|
|
||||||
graphql({ schema, source }).then((result) => { |
|
||||||
// Prints
|
|
||||||
// {
|
|
||||||
// data: { hello: "world" }
|
|
||||||
// }
|
|
||||||
console.log(result); |
|
||||||
}); |
|
@ -1,6 +0,0 @@ |
|||||||
{ |
|
||||||
"dev": { |
|
||||||
"projectId": "dooku_bac0", |
|
||||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InVzZXJAbm9jb2RiLmNvbSIsImZpcnN0bmFtZSI6bnVsbCwibGFzdG5hbWUiOm51bGwsImlkIjoxLCJyb2xlcyI6InVzZXIiLCJpYXQiOjE2NDA0MjA4MzN9.Ixh4PRNVoCV_nCq6amKB7EjRJO90_iMxF2LXaHDA6W4\neyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InVzZXJAbm9jb2RiLmNvbSIsImZpcnN0bmFtZSI6bnVsbCwibGFzdG5hbWUiOm51bGwsImlkIjoxLCJyb2xlcyI6InVzZXIiLCJpYXQiOjE2NDA0MjA4MzN9.Ixh4PRNVoCV_nCq6amKB7EjRJO90_iMxF2LXaHDA6W4\neyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InVzZXJAbm9jb2RiLmNvbSIsImZpcnN0bmFtZSI6bnVsbCwibGFzdG5hbWUiOm51bGwsImlkIjoxLCJyb2xlcyI6InVzZXIiLCJpYXQiOjE2NDA0MjA4MzN9.Ixh4PRNVoCV_nCq6amKB7EjRJO90_iMxF2LXaHDA6W4\neyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InVzZXJAbm9jb2RiLmNvbSIsImZpcnN0bmFtZSI6bnVsbCwibGFzdG5hbWUiOm51bGwsImlkIjoxLCJyb2xlcyI6InVzZXIiLCJpYXQiOjE2NDA0MjA4MzN9.Ixh4PRNVoCV_nCq6amKB7EjRJO90_iMxF2LXaHDA6W4\neyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InVzZXJAbm9jb2RiLmNvbSIsImZpcnN0bmFtZSI6bnVsbCwibGFzdG5hbWUiOm51bGwsImlkIjoxLCJyb2xlcyI6InVzZXIiLCJpYXQiOjE2NDA0MjA4MzN9.Ixh4PRNVoCV_nCq6amKB7EjRJO90_iMxF2LXaHDA6W4" |
|
||||||
} |
|
||||||
} |
|
File diff suppressed because one or more lines are too long
@ -1,336 +0,0 @@ |
|||||||
const delay = t => new Promise(res => setTimeout(() => res(), t)) |
|
||||||
|
|
||||||
const time = 1000; |
|
||||||
const knex = require('knex')({ |
|
||||||
client: 'mysql2', |
|
||||||
connection: { |
|
||||||
user: 'root', |
|
||||||
password: 'password', |
|
||||||
database: 'sakila' |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
|
|
||||||
class Country { |
|
||||||
constructor(data) { |
|
||||||
Object.assign(this, data) |
|
||||||
} |
|
||||||
|
|
||||||
async cityList() { |
|
||||||
return (await knex('city').select('*').where({ |
|
||||||
country_id: this.country_id |
|
||||||
}).limit(1)).map(c => new City(c)) |
|
||||||
} |
|
||||||
|
|
||||||
async cityCount() { |
|
||||||
return (await knex('city').count('city_id as count').where({ |
|
||||||
country_id: this.country_id |
|
||||||
}).first()).count |
|
||||||
} |
|
||||||
|
|
||||||
async addressCount(args) { |
|
||||||
|
|
||||||
return await Promise.all(args.map(c => c.addressCount())) |
|
||||||
|
|
||||||
// return (await knex('city').count('city_id as count').where({
|
|
||||||
// country_id: this.country_id
|
|
||||||
// }).first()).count
|
|
||||||
} |
|
||||||
|
|
||||||
async addressList(args) { |
|
||||||
|
|
||||||
return await Promise.all((await this.cityList()).map(c => c.addressList())) |
|
||||||
|
|
||||||
// return (await knex('city').count('city_id as count').where({
|
|
||||||
// country_id: this.country_id
|
|
||||||
// }).first()).count
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
class Address { |
|
||||||
constructor(data) { |
|
||||||
Object.assign(this, data) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
class City { |
|
||||||
constructor(data) { |
|
||||||
Object.assign(this, data) |
|
||||||
} |
|
||||||
|
|
||||||
async countryRead() { |
|
||||||
return new Country((await knex('country').select('*').where({ |
|
||||||
country_id: this.country_id |
|
||||||
}).first())) |
|
||||||
} |
|
||||||
|
|
||||||
/* async addressList() { |
|
||||||
return (await knex('address').select('*').where({ |
|
||||||
city_id: this.city_id |
|
||||||
})).map(c => new Address(c)) |
|
||||||
}*/ |
|
||||||
|
|
||||||
/* async addressCount() { |
|
||||||
return (await knex('address').count('city_id as count').where({ |
|
||||||
city_id: this.city_id |
|
||||||
}).first()).count |
|
||||||
}*/ |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
// const nestResolver = {
|
|
||||||
// country: `INDIA`,
|
|
||||||
// async cityCount() {
|
|
||||||
// await delay(time)
|
|
||||||
// return `12-${time}`
|
|
||||||
// },
|
|
||||||
// async cityList() {
|
|
||||||
// await delay(time)
|
|
||||||
// return {
|
|
||||||
// city: `city 1-${time}`,
|
|
||||||
// async addressCount() {
|
|
||||||
// await delay(time)
|
|
||||||
// return `2-${time}`
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
|
|
||||||
const rootAst = { |
|
||||||
Country: { |
|
||||||
name: 'Country', |
|
||||||
type: 'Object', |
|
||||||
fields: { |
|
||||||
country_id: { |
|
||||||
name: 'country_id', |
|
||||||
type: 'number' |
|
||||||
}, country: { |
|
||||||
name: 'country', |
|
||||||
type: 'string' |
|
||||||
}, cityList: { |
|
||||||
name: 'cityList', |
|
||||||
type: 'array', |
|
||||||
elementType: 'City', |
|
||||||
nested: { |
|
||||||
level: 1, dependsOn: ['country_id'] |
|
||||||
} |
|
||||||
}, cityCount: { |
|
||||||
name: 'cityCount', |
|
||||||
type: 'number', |
|
||||||
nested: { |
|
||||||
level: 1, dependsOn: ['country_id'] |
|
||||||
} |
|
||||||
}, addressCount: { |
|
||||||
name: 'addressCount', |
|
||||||
type: 'number', |
|
||||||
nested: {level: 2, dependsOn: ['cityList', 'addressCount']} |
|
||||||
} |
|
||||||
} |
|
||||||
}, City: { |
|
||||||
name: 'City', |
|
||||||
type: 'Object', |
|
||||||
fields: { |
|
||||||
city_id: { |
|
||||||
name: 'city_id', |
|
||||||
type: 'number' |
|
||||||
}, country_id: { |
|
||||||
name: 'country_id', |
|
||||||
type: 'number' |
|
||||||
}, city: { |
|
||||||
name: 'city', |
|
||||||
type: 'string' |
|
||||||
}, addressList: { |
|
||||||
name: 'addressList', |
|
||||||
type: 'array', |
|
||||||
elementType: 'City' |
|
||||||
}, countryRead: { |
|
||||||
name: 'countryRead', |
|
||||||
type: 'Country' |
|
||||||
}, addressCount: { |
|
||||||
name: 'addressCount', |
|
||||||
type: 'number' |
|
||||||
} |
|
||||||
} |
|
||||||
}, Address: { |
|
||||||
name: 'Address', |
|
||||||
type: 'Object', |
|
||||||
fields: { |
|
||||||
address: { |
|
||||||
name: 'address', |
|
||||||
type: 'string' |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
|
||||||
CountryList: { |
|
||||||
type: 'array', |
|
||||||
elementType: 'Country' |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
const nestResolver = { |
|
||||||
async Country() { |
|
||||||
return new Country(await knex('country').first()) |
|
||||||
}, |
|
||||||
|
|
||||||
async CountryList() { |
|
||||||
return (await knex('country').limit(10)).map(c => new Country(c)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
const req = { |
|
||||||
CountryList: { |
|
||||||
country: 1, |
|
||||||
cityList: { |
|
||||||
addressCount: 1 |
|
||||||
}, |
|
||||||
addressCount: 1, |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
const reqExecutor = async (reqObj, resObj, _ast) => { |
|
||||||
|
|
||||||
const res = {} |
|
||||||
// const dependedFields = Object.keys(reqObj).map(k => (ast.fields && ast.fields[k] && ast.fields[k].nested && ast.fields[k].nested.dependsOn))
|
|
||||||
/* const dependFields = new Set(); |
|
||||||
for(const k of Object.keys(reqObj)){ |
|
||||||
if(ast.fields && ast.fields[k] && ast.fields[k].nested && ast.fields[k].nested.dependsOn){ |
|
||||||
dependFields.add(ast.fields[k].nested.dependsOn) |
|
||||||
|
|
||||||
} |
|
||||||
}*/ |
|
||||||
|
|
||||||
function extractDependsOn(key, i = 0, prev, __ast) { |
|
||||||
const ast = __ast || _ast |
|
||||||
if (!prev || typeof prev !== 'object' || i >= _ast.fields[key].nested.dependsOn.length) { |
|
||||||
if (typeof resObj[key] === 'function') { |
|
||||||
return resObj[key](prev)//.call(res);
|
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} else if (typeof resObj[key] === 'object') { |
|
||||||
return Promise.resolve(resObj[key]) |
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} else { |
|
||||||
return Promise.resolve(resObj[key]) |
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} |
|
||||||
} |
|
||||||
if (typeof prev[key] === 'function') { |
|
||||||
return resObj[key](res)//.call(res);
|
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
if (!prev[_ast.fields[key].nested.dependsOn[i]]) |
|
||||||
extractField(_ast.fields[key].nested.dependsOn[i], _ast) |
|
||||||
prev[key] = prev[_ast.fields[key].nested.dependsOn[i]].then(next => { |
|
||||||
// return extractDependsOn(key, ++i, next)
|
|
||||||
|
|
||||||
|
|
||||||
if (Array.isArray(next)) { |
|
||||||
return Promise.all(next.map(r => extractDependsOn(key, ++i, r, rootAst[_ast[ast.fields[key].nested.dependsOn[i]].elementType]))) |
|
||||||
} else { |
|
||||||
return extractDependsOn(reqObj[key], ++i, next, _ast[ast.fields[key].nested.dependsOn[i]]) |
|
||||||
} |
|
||||||
|
|
||||||
}) |
|
||||||
return prev[key] |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
function extractField(key, __ast) { |
|
||||||
const ast = __ast || _ast |
|
||||||
|
|
||||||
if (!(ast.fields && ast.fields[key] && ast.fields[key].nested)) { |
|
||||||
if (typeof resObj[key] === 'function') { |
|
||||||
res[key] = resObj[key]()//.call(res);
|
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} else if (typeof resObj[key] === 'object') { |
|
||||||
res[key] = Promise.resolve(resObj[key]) |
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} else { |
|
||||||
res[key] = Promise.resolve(resObj[key]) |
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} |
|
||||||
} else { |
|
||||||
extractDependsOn(key, 0, res) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
for (const key of Object.keys(reqObj)) { |
|
||||||
if (key in resObj) { |
|
||||||
extractField(key); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
if (reqObj[key] && typeof reqObj[key] === 'object') { |
|
||||||
res[key] = res[key].then(res1 => { |
|
||||||
if (Array.isArray(res1)) { |
|
||||||
return Promise.all(res1.map(r => reqExecutor(reqObj[key], r, rootAst[_ast[key].elementType]))) |
|
||||||
} else { |
|
||||||
return reqExecutor(reqObj[key], res1, _ast[key]) |
|
||||||
} |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
await Promise.all(Object.values(res)) |
|
||||||
|
|
||||||
const out = {}; |
|
||||||
for (const [k, v] of Object.entries(res)) { |
|
||||||
out[k] = await v |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
return out |
|
||||||
} |
|
||||||
|
|
||||||
(async () => { |
|
||||||
console.time('start') |
|
||||||
console.log(JSON.stringify(await reqExecutor(req, nestResolver, rootAst), 0, 2)); |
|
||||||
console.timeEnd('start') |
|
||||||
})().catch(e => console.log(e)).finally(() => process.exit(0)) |
|
||||||
|
|
||||||
|
|
||||||
const reqExecutorOld = async (reqObj, resObj, ast) => { |
|
||||||
|
|
||||||
const res = {} |
|
||||||
|
|
||||||
|
|
||||||
await Promise.all(Object.keys(reqObj).map(async (key) => { |
|
||||||
if (key in resObj && !(ast.fields && ast.fields[key] && ast.fields[key].dependsOn)) { |
|
||||||
|
|
||||||
if (typeof resObj[key] === 'function') { |
|
||||||
res[key] = await resObj[key]()//.call(res);
|
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} else if (typeof resObj[key] === 'object') { |
|
||||||
res[key] = resObj[key] |
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} else { |
|
||||||
res[key] = resObj[key] |
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} |
|
||||||
await delay(100) |
|
||||||
} |
|
||||||
if (reqObj[key] && typeof reqObj[key] === 'object') { |
|
||||||
if (Array.isArray(res[key])) { |
|
||||||
res[key] = await Promise.all(res[key].map(r => reqExecutor(reqObj[key], r, rootAst[ast[key].elementType]))) |
|
||||||
} else { |
|
||||||
res[key] = await reqExecutor(reqObj[key], res[key], ast[key]) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
})) |
|
||||||
|
|
||||||
return res |
|
||||||
} |
|
@ -1,365 +0,0 @@ |
|||||||
const {expect} = require('chai'); |
|
||||||
require('mocha'); |
|
||||||
const knex = require('knex')({ |
|
||||||
client: 'mysql2', |
|
||||||
connection: { "user": "root", |
|
||||||
"password": "password", |
|
||||||
"port":"3306", |
|
||||||
"host":"localhost", |
|
||||||
database: 'sakila' |
|
||||||
} |
|
||||||
}); |
|
||||||
const delay = t => new Promise(res => setTimeout(() => res(), t)) |
|
||||||
|
|
||||||
const simpleInOut = require('./testData/01_simple') |
|
||||||
const compareObj = require('./compareObj') |
|
||||||
const Dataloader = require("dataloader"); |
|
||||||
|
|
||||||
const groupBy = (arr, field) => { |
|
||||||
return (arr || []).reduce((obj, o) => { |
|
||||||
obj[o[field]] = obj[o[field]] || [] |
|
||||||
obj[o[field]].push(o) |
|
||||||
return obj; |
|
||||||
}, {}) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
const cityListLoader = new Dataloader(async function (ids) { |
|
||||||
const cities = await knex('city').select('*').whereIn('country_id', ids); |
|
||||||
const gbObj = groupBy(cities, 'country_id') |
|
||||||
return ids.map(id => (gbObj[id] || []).map(c => new City(c))) |
|
||||||
}) |
|
||||||
|
|
||||||
|
|
||||||
const addressCountLoader = new Dataloader(async function (ids) { |
|
||||||
const cities = await knex('address').select('city_id').count('address_id as count') |
|
||||||
.groupBy('city_id') |
|
||||||
.whereIn('city_id', ids); |
|
||||||
const gbObj = groupBy(cities, 'city_id') |
|
||||||
return ids.map(id => (gbObj[id] && gbObj[id][0] && gbObj[id][0].count)) |
|
||||||
}) |
|
||||||
const cityCountLoader = new Dataloader(async function (ids) { |
|
||||||
const cities = await knex('city').select('country').count('city_id as count') |
|
||||||
.groupBy('country_id') |
|
||||||
.whereIn('country_id', ids); |
|
||||||
const gbObj = groupBy(cities, 'country_id') |
|
||||||
return ids.map(id => (gbObj[id] && gbObj[id][0] && gbObj[id][0].count)) |
|
||||||
}) |
|
||||||
|
|
||||||
const addressListLoader = new Dataloader(async function (ids) { |
|
||||||
const addresses = await knex('address').select('*').whereIn('city_id', ids); |
|
||||||
const gbObj = groupBy(addresses, 'city_id') |
|
||||||
return ids.map(id => (gbObj[id] || []).map(c => new Address(c))) |
|
||||||
}) |
|
||||||
|
|
||||||
const cityLoader = new Dataloader(async function (ids) { |
|
||||||
const cities = await knex('city').select('*').whereIn('city_id', ids); |
|
||||||
const gbObj = groupBy(cities, 'city_id') |
|
||||||
return ids.map(id => gbObj[id] && gbObj[id][0] ? new City(gbObj[id][0]) : null) |
|
||||||
}) |
|
||||||
const countryLoader = new Dataloader(async function (ids) { |
|
||||||
const countries = await knex('country').select('*').whereIn('country_id', ids); |
|
||||||
const gbObj = groupBy(countries, 'country_id') |
|
||||||
return ids.map(id => gbObj[id] && gbObj[id][0] ? new Country(gbObj[id][0]) : null) |
|
||||||
}) |
|
||||||
|
|
||||||
class Country { |
|
||||||
constructor(data) { |
|
||||||
Object.assign(this, data) |
|
||||||
} |
|
||||||
|
|
||||||
async cityList() { |
|
||||||
return await cityListLoader.load(this.country_id) |
|
||||||
} |
|
||||||
|
|
||||||
async cityCount() { |
|
||||||
return cityCountLoader.load(this.country_id) |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
class Address { |
|
||||||
constructor(data) { |
|
||||||
Object.assign(this, data) |
|
||||||
} |
|
||||||
|
|
||||||
async City() { |
|
||||||
return cityLoader.load(this.city_id) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
class City { |
|
||||||
constructor(data) { |
|
||||||
Object.assign(this, data) |
|
||||||
} |
|
||||||
|
|
||||||
async Country() { |
|
||||||
return countryLoader.load(this.country_id) |
|
||||||
} |
|
||||||
|
|
||||||
async addressList() { |
|
||||||
return addressListLoader.load(this.city_id) |
|
||||||
} |
|
||||||
|
|
||||||
async addressCount() { |
|
||||||
return addressCountLoader.load(this.city_id) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
const rootAst = { |
|
||||||
Country: { |
|
||||||
name: 'Country', |
|
||||||
type: 'Object', |
|
||||||
fields: { |
|
||||||
country_id: { |
|
||||||
name: 'country_id', |
|
||||||
type: 'number' |
|
||||||
}, country: { |
|
||||||
name: 'country', |
|
||||||
type: 'string' |
|
||||||
}, |
|
||||||
|
|
||||||
cityList: { |
|
||||||
name: 'cityList', |
|
||||||
type: 'array', |
|
||||||
elementType: 'City', |
|
||||||
// nested: {
|
|
||||||
// level: 1, path: ['country_id']
|
|
||||||
// }
|
|
||||||
}, |
|
||||||
cityCount: { |
|
||||||
name: 'cityCount', |
|
||||||
type: 'number', |
|
||||||
// nested: {
|
|
||||||
// level: 1, path: ['country_id']
|
|
||||||
// }
|
|
||||||
}, |
|
||||||
|
|
||||||
|
|
||||||
addressCount: { |
|
||||||
name: 'addressCount', |
|
||||||
type: 'number', |
|
||||||
nested: {level: 2, path: ['cityList', 'addressCount']} |
|
||||||
}, |
|
||||||
addressList: { |
|
||||||
type: 'number', |
|
||||||
nested: {level: 2, path: ['cityList', 'addressList', 'address']} |
|
||||||
}, |
|
||||||
d: { |
|
||||||
type: 'number', |
|
||||||
nested: {level: 2, path: ['cityList', 'addressList', 'City', 'Country']} |
|
||||||
} |
|
||||||
} |
|
||||||
}, City: { |
|
||||||
name: 'City', |
|
||||||
type: 'Object', |
|
||||||
fields: { |
|
||||||
city_id: { |
|
||||||
name: 'city_id', |
|
||||||
type: 'number' |
|
||||||
}, country_id: { |
|
||||||
name: 'country_id', |
|
||||||
type: 'number' |
|
||||||
}, city: { |
|
||||||
name: 'city', |
|
||||||
type: 'string' |
|
||||||
}, addressList: { |
|
||||||
name: 'addressList', |
|
||||||
type: 'array', |
|
||||||
elementType: 'Address' |
|
||||||
}, countryRead: { |
|
||||||
name: 'countryRead', |
|
||||||
type: 'Country' |
|
||||||
}, addressCount: { |
|
||||||
name: 'addressCount', |
|
||||||
type: 'number' |
|
||||||
}, |
|
||||||
Country: { |
|
||||||
name: 'Country', |
|
||||||
type: 'Country', |
|
||||||
// nested: {
|
|
||||||
// level: 1, path: ['country_id']
|
|
||||||
// }
|
|
||||||
}, |
|
||||||
} |
|
||||||
}, Address: { |
|
||||||
name: 'Address', |
|
||||||
type: 'Object', |
|
||||||
fields: { |
|
||||||
address: { |
|
||||||
name: 'address', |
|
||||||
type: 'string' |
|
||||||
}, |
|
||||||
City: { |
|
||||||
name: 'City', |
|
||||||
type: 'City', |
|
||||||
// nested: {
|
|
||||||
// level: 1,
|
|
||||||
// path: ['city_id']
|
|
||||||
// }
|
|
||||||
}, |
|
||||||
CountryName: { |
|
||||||
name: 'Country', |
|
||||||
type: 'Country', |
|
||||||
nested: { |
|
||||||
level: 1, |
|
||||||
path: ['City', 'Country', 'country'] |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
|
||||||
CountryList: { |
|
||||||
type: 'array', |
|
||||||
elementType: 'Country' |
|
||||||
}, |
|
||||||
AddressList: { |
|
||||||
type: 'array', |
|
||||||
elementType: 'Address' |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
const nestResolver = { |
|
||||||
async Country() { |
|
||||||
return new Country(await knex('country').first()) |
|
||||||
}, |
|
||||||
|
|
||||||
async CountryList() { |
|
||||||
return (await knex('country').limit(2)).map(c => new Country(c)) |
|
||||||
}, |
|
||||||
|
|
||||||
async AddressList() { |
|
||||||
return (await knex('address').limit(10)).map(c => new Address(c)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
const flattenArray = (res) => { |
|
||||||
return Array.isArray(res) ? res.flatMap(r => flattenArray(r)) : res |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
const execute = async (requestObj, resolverObj, ast, objTree = {}) => { |
|
||||||
|
|
||||||
const res = [] |
|
||||||
// extract nested(lookup) recursively
|
|
||||||
const extractNested = (path, o = {}, resolver) => { |
|
||||||
if (path.length) { |
|
||||||
const key = path[0] |
|
||||||
if (!o[key]) { |
|
||||||
|
|
||||||
|
|
||||||
if (typeof resolver[key] === 'function') { |
|
||||||
o[path[0]] = resolver[key]()//.call(res);
|
|
||||||
// console.log(prefix + o[path[0]])
|
|
||||||
} else if (typeof resolver[key] === 'object') { |
|
||||||
o[path[0]] = Promise.resolve(resolver[key]) |
|
||||||
// console.log(prefix + o[path[0]])
|
|
||||||
} else { |
|
||||||
o[path[0]] = Promise.resolve(resolver[key]) |
|
||||||
// console.log(prefix + o[path[0]])
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} else if (typeof o[key] === 'function') { |
|
||||||
o[key] = o[key]() |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
return (o[path[0]] instanceof Promise ? o[path[0]] : Promise.resolve(o[path[0]])).then(res1 => { |
|
||||||
|
|
||||||
if (Array.isArray(res1)) { |
|
||||||
return Promise.all(res1.map(r => extractNested(path.slice(1), r))) |
|
||||||
} else { |
|
||||||
return extractNested(path.slice(1), res1) |
|
||||||
} |
|
||||||
|
|
||||||
}) |
|
||||||
} else { |
|
||||||
return Promise.resolve(o) |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
// function for extracting field
|
|
||||||
function extractField(key) { |
|
||||||
if (!(ast && ast && ast[key] && ast[key].nested)) { |
|
||||||
if (resolverObj) { |
|
||||||
// resolve if it's resolver function
|
|
||||||
if (typeof resolverObj[key] === 'function') { |
|
||||||
res[key] = resolverObj[key]()//.call(res);
|
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} else if (typeof resolverObj[key] === 'object') { |
|
||||||
res[key] = Promise.resolve(resolverObj[key]) |
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} else { |
|
||||||
res[key] = Promise.resolve(resolverObj[key]) |
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
objTree[key] = res[key] |
|
||||||
|
|
||||||
} else { |
|
||||||
|
|
||||||
// if nested extract the nested value
|
|
||||||
res[key] = extractNested(ast[key].nested.path, objTree, resolverObj).then(res => { |
|
||||||
return Promise.resolve(flattenArray(res)) |
|
||||||
}) |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
for (const key of Object.keys(requestObj)) { |
|
||||||
extractField(key); |
|
||||||
|
|
||||||
if (requestObj[key] && typeof requestObj[key] === 'object') { |
|
||||||
res[key] = res[key].then(res1 => { |
|
||||||
if (Array.isArray(res1)) { |
|
||||||
return Promise.all(res1.map(r => execute(requestObj[key], r, rootAst[ast[key].elementType] && rootAst[ast[key].elementType].fields, objTree[key] = {}))) |
|
||||||
} else { |
|
||||||
return execute(requestObj[key], res1, ast[key].fields, objTree[key] = {}) |
|
||||||
} |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
await Promise.all(Object.values(res)) |
|
||||||
|
|
||||||
const out = {}; |
|
||||||
for (const [k, v] of Object.entries(res)) { |
|
||||||
if (k in requestObj) |
|
||||||
out[k] = await v |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
return out |
|
||||||
} |
|
||||||
|
|
||||||
describe('Resolver', async () => { |
|
||||||
for (const {name, in: input, out} of simpleInOut) { |
|
||||||
it(`Resolver : ${name}`, async function () { |
|
||||||
this.timeout(50000) |
|
||||||
console.time(name) |
|
||||||
const res = await execute(input, nestResolver, rootAst); |
|
||||||
console.timeEnd(name) |
|
||||||
console.log(JSON.stringify(res, 0, 2)) |
|
||||||
expect(compareObj(out, res)).to.be.eq(true) |
|
||||||
}); |
|
||||||
} |
|
||||||
}) |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,556 +0,0 @@ |
|||||||
const delay = t => new Promise(res => setTimeout(() => res(), t)) |
|
||||||
const Dataloader = require('dataloader') |
|
||||||
const time = 1000; |
|
||||||
const knex = require('knex')({ |
|
||||||
client: 'mysql2', |
|
||||||
connection: { |
|
||||||
user: 'root', |
|
||||||
password: 'password', |
|
||||||
database: 'sakila' |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Country { |
|
||||||
constructor(data) { |
|
||||||
Object.assign(this, data) |
|
||||||
} |
|
||||||
|
|
||||||
async cityList() { |
|
||||||
return (await knex('city').select('*').where({ |
|
||||||
country_id: this.country_id |
|
||||||
}).limit(1)).map(c => new City(c)) |
|
||||||
} |
|
||||||
|
|
||||||
async cityCount() { |
|
||||||
return (await knex('city').count('city_id as count').where({ |
|
||||||
country_id: this.country_id |
|
||||||
}).first()).count |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
class Address { |
|
||||||
constructor(data) { |
|
||||||
Object.assign(this, data) |
|
||||||
} |
|
||||||
|
|
||||||
async City() { |
|
||||||
return new City(await knex('city').select('*').where({ |
|
||||||
city_id: this.city_id |
|
||||||
}).first()) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
class City { |
|
||||||
constructor(data) { |
|
||||||
Object.assign(this, data) |
|
||||||
} |
|
||||||
|
|
||||||
async Country() { |
|
||||||
return new Country((await knex('country').select('*').where({ |
|
||||||
country_id: this.country_id |
|
||||||
}).first())) |
|
||||||
} |
|
||||||
|
|
||||||
async addressList() { |
|
||||||
return (await knex('address').select('*').where({ |
|
||||||
city_id: this.city_id |
|
||||||
})).map(c => new Address(c)) |
|
||||||
} |
|
||||||
|
|
||||||
async addressCount() { |
|
||||||
return (await knex('address').count('city_id as count').where({ |
|
||||||
city_id: this.city_id |
|
||||||
}).first()).count |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
// const nestResolver = {
|
|
||||||
// country: `INDIA`,
|
|
||||||
// async cityCount() {
|
|
||||||
// await delay(time)
|
|
||||||
// return `12-${time}`
|
|
||||||
// },
|
|
||||||
// async cityList() {
|
|
||||||
// await delay(time)
|
|
||||||
// return {
|
|
||||||
// city: `city 1-${time}`,
|
|
||||||
// async addressCount() {
|
|
||||||
// await delay(time)
|
|
||||||
// return `2-${time}`
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
|
|
||||||
const rootAst = { |
|
||||||
Country: { |
|
||||||
name: 'Country', |
|
||||||
type: 'Object', |
|
||||||
fields: { |
|
||||||
country_id: { |
|
||||||
name: 'country_id', |
|
||||||
type: 'number' |
|
||||||
}, country: { |
|
||||||
name: 'country', |
|
||||||
type: 'string' |
|
||||||
}, |
|
||||||
|
|
||||||
cityList: { |
|
||||||
name: 'cityList', |
|
||||||
type: 'array', |
|
||||||
elementType: 'City', |
|
||||||
// nested: {
|
|
||||||
// level: 1, path: ['country_id']
|
|
||||||
// }
|
|
||||||
}, |
|
||||||
cityCount: { |
|
||||||
name: 'cityCount', |
|
||||||
type: 'number', |
|
||||||
// nested: {
|
|
||||||
// level: 1, path: ['country_id']
|
|
||||||
// }
|
|
||||||
}, |
|
||||||
|
|
||||||
|
|
||||||
addressCount: { |
|
||||||
name: 'addressCount', |
|
||||||
type: 'number', |
|
||||||
nested: {level: 2, path: ['cityList', 'addressCount']} |
|
||||||
}, |
|
||||||
c: { |
|
||||||
name: 'addressCount', |
|
||||||
type: 'number', |
|
||||||
nested: {level: 2, path: ['cityList', 'addressList','address']} |
|
||||||
}, |
|
||||||
d: { |
|
||||||
name: 'addressCount', |
|
||||||
type: 'number', |
|
||||||
nested: {level: 2, path: ['cityList', 'addressList','City', 'Country']} |
|
||||||
} |
|
||||||
} |
|
||||||
}, City: { |
|
||||||
name: 'City', |
|
||||||
type: 'Object', |
|
||||||
fields: { |
|
||||||
city_id: { |
|
||||||
name: 'city_id', |
|
||||||
type: 'number' |
|
||||||
}, country_id: { |
|
||||||
name: 'country_id', |
|
||||||
type: 'number' |
|
||||||
}, city: { |
|
||||||
name: 'city', |
|
||||||
type: 'string' |
|
||||||
}, addressList: { |
|
||||||
name: 'addressList', |
|
||||||
type: 'array', |
|
||||||
elementType: 'Address' |
|
||||||
}, countryRead: { |
|
||||||
name: 'countryRead', |
|
||||||
type: 'Country' |
|
||||||
}, addressCount: { |
|
||||||
name: 'addressCount', |
|
||||||
type: 'number' |
|
||||||
}, |
|
||||||
Country: { |
|
||||||
name: 'Country', |
|
||||||
type: 'Country', |
|
||||||
// nested: {
|
|
||||||
// level: 1, path: ['country_id']
|
|
||||||
// }
|
|
||||||
}, |
|
||||||
} |
|
||||||
}, Address: { |
|
||||||
name: 'Address', |
|
||||||
type: 'Object', |
|
||||||
fields: { |
|
||||||
address: { |
|
||||||
name: 'address', |
|
||||||
type: 'string' |
|
||||||
}, |
|
||||||
City: { |
|
||||||
name: 'City', |
|
||||||
type: 'City', |
|
||||||
// nested: {
|
|
||||||
// level: 1,
|
|
||||||
// path: ['city_id']
|
|
||||||
// }
|
|
||||||
}, |
|
||||||
Country: { |
|
||||||
name: 'Country', |
|
||||||
type: 'Country', |
|
||||||
nested: { |
|
||||||
level: 1, |
|
||||||
path: ['City', 'Country','country'] |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
|
||||||
CountryList: { |
|
||||||
type: 'array', |
|
||||||
elementType: 'Country' |
|
||||||
}, |
|
||||||
AddressList: { |
|
||||||
type: 'array', |
|
||||||
elementType: 'Address' |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
const nestResolver = { |
|
||||||
async Country() { |
|
||||||
return new Country(await knex('country').first()) |
|
||||||
}, |
|
||||||
|
|
||||||
async CountryList() { |
|
||||||
return (await knex('country').limit(2)).map(c => new Country(c)) |
|
||||||
}, |
|
||||||
|
|
||||||
async AddressList() { |
|
||||||
return (await knex('address').limit(10)).map(c => new Address(c)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/* |
|
||||||
|
|
||||||
const reqExecutor = async (reqObj, resObj, _ast) => { |
|
||||||
|
|
||||||
const res = {} |
|
||||||
// const dependedFields = Object.keys(reqObj).map(k => (ast.fields && ast.fields[k] && ast.fields[k].nested && ast.fields[k].nested.path))
|
|
||||||
/!* const dependFields = new Set(); |
|
||||||
for(const k of Object.keys(reqObj)){ |
|
||||||
if(ast.fields && ast.fields[k] && ast.fields[k].nested && ast.fields[k].nested.path){ |
|
||||||
dependFields.add(ast.fields[k].nested.path) |
|
||||||
|
|
||||||
} |
|
||||||
}*!/ |
|
||||||
|
|
||||||
function extractDependsOn(key, i = 0, prev, __ast) { |
|
||||||
const ast = __ast || _ast |
|
||||||
if (!prev || typeof prev !== 'object' || i >= _ast.fields[key].nested.path.length) { |
|
||||||
if (typeof resObj[key] === 'function') { |
|
||||||
return resObj[key](prev)//.call(res);
|
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} else if (typeof resObj[key] === 'object') { |
|
||||||
return Promise.resolve(resObj[key]) |
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} else { |
|
||||||
return Promise.resolve(resObj[key]) |
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} |
|
||||||
} |
|
||||||
if (typeof prev[key] === 'function') { |
|
||||||
return resObj[key](res)//.call(res);
|
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
if (!prev[_ast.fields[key].nested.path[i]]) |
|
||||||
extractField(_ast.fields[key].nested.path[i], _ast) |
|
||||||
prev[key] = prev[_ast.fields[key].nested.path[i]].then(next => { |
|
||||||
// return extractDependsOn(key, ++i, next)
|
|
||||||
|
|
||||||
|
|
||||||
if (Array.isArray(next)) { |
|
||||||
return Promise.all(next.map(r => extractDependsOn(key, ++i, r, rootAst[_ast[ast.fields[key].nested.path[i]].elementType]))) |
|
||||||
} else { |
|
||||||
return extractDependsOn(reqObj[key], ++i, next, _ast[ast.fields[key].nested.path[i]]) |
|
||||||
} |
|
||||||
|
|
||||||
}) |
|
||||||
return prev[key] |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
function extractField(key, __ast) { |
|
||||||
const ast = __ast || _ast |
|
||||||
|
|
||||||
if (!(ast.fields && ast.fields[key] && ast.fields[key].nested)) { |
|
||||||
if (typeof resObj[key] === 'function') { |
|
||||||
res[key] = resObj[key]()//.call(res);
|
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} else if (typeof resObj[key] === 'object') { |
|
||||||
res[key] = Promise.resolve(resObj[key]) |
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} else { |
|
||||||
res[key] = Promise.resolve(resObj[key]) |
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} |
|
||||||
} else { |
|
||||||
extractDependsOn(key, 0, res) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
for (const key of Object.keys(reqObj)) { |
|
||||||
if (key in resObj) { |
|
||||||
extractField(key); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
if (reqObj[key] && typeof reqObj[key] === 'object') { |
|
||||||
res[key] = res[key].then(res1 => { |
|
||||||
if (Array.isArray(res1)) { |
|
||||||
return Promise.all(res1.map(r => reqExecutor(reqObj[key], r, rootAst[_ast[key].elementType]))) |
|
||||||
} else { |
|
||||||
return reqExecutor(reqObj[key], res1, _ast[key]) |
|
||||||
} |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
await Promise.all(Object.values(res)) |
|
||||||
|
|
||||||
const out = {}; |
|
||||||
for (const [k, v] of Object.entries(res)) { |
|
||||||
out[k] = await v |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
return out |
|
||||||
} |
|
||||||
*/ |
|
||||||
|
|
||||||
/* |
|
||||||
const reqExecutor = async (reqObj, resObj, ast) => { |
|
||||||
|
|
||||||
const res = {} |
|
||||||
|
|
||||||
|
|
||||||
await Promise.all(Object.keys(reqObj).map(async (key) => { |
|
||||||
if (key in resObj && !(ast.fields && ast.fields[key] && ast.fields[key].path)) { |
|
||||||
|
|
||||||
if (typeof resObj[key] === 'function') { |
|
||||||
res[key] = await resObj[key]()//.call(res);
|
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} else if (typeof resObj[key] === 'object') { |
|
||||||
res[key] = resObj[key] |
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} else { |
|
||||||
res[key] = resObj[key] |
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} |
|
||||||
await delay(100) |
|
||||||
} |
|
||||||
if (reqObj[key] && typeof reqObj[key] === 'object') { |
|
||||||
if (Array.isArray(res[key])) { |
|
||||||
res[key] = await Promise.all(res[key].map(r => reqExecutor(reqObj[key], r, rootAst[ast[key].elementType]))) |
|
||||||
} else { |
|
||||||
res[key] = await reqExecutor(reqObj[key], res[key], ast[key]) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
})) |
|
||||||
|
|
||||||
return res |
|
||||||
} |
|
||||||
|
|
||||||
*/ |
|
||||||
|
|
||||||
|
|
||||||
const execute = async (requestObj, resolverObj, ast) => { |
|
||||||
|
|
||||||
const res = {} |
|
||||||
// const dependedFields = Object.keys(reqObj).map(k => (ast.fields && ast.fields[k] && ast.fields[k].nested && ast.fields[k].nested.path))
|
|
||||||
/* const dependFields = new Set(); |
|
||||||
for(const k of Object.keys(reqObj)){ |
|
||||||
if(ast.fields && ast.fields[k] && ast.fields[k].nested && ast.fields[k].nested.path){ |
|
||||||
dependFields.add(ast.fields[k].nested.path) |
|
||||||
|
|
||||||
} |
|
||||||
}*/ |
|
||||||
|
|
||||||
|
|
||||||
// extract nested(lookup) recursively
|
|
||||||
const extractNested = (path, o = {}, resolver) => { |
|
||||||
if (path.length) { |
|
||||||
const key = path[0] |
|
||||||
if (!o[key]) { |
|
||||||
|
|
||||||
|
|
||||||
if (typeof resolver[key] === 'function') { |
|
||||||
o[path[0]] = resolver[key]()//.call(res);
|
|
||||||
// console.log(prefix + o[path[0]])
|
|
||||||
} else if (typeof resolver[key] === 'object') { |
|
||||||
o[path[0]] = Promise.resolve(resolver[key]) |
|
||||||
// console.log(prefix + o[path[0]])
|
|
||||||
} else { |
|
||||||
o[path[0]] = Promise.resolve(resolver[key]) |
|
||||||
// console.log(prefix + o[path[0]])
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} else if (typeof o[key] === 'function') { |
|
||||||
o[key] = o[key]() |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
return (o[path[0]] instanceof Promise ? o[path[0]] : Promise.resolve(o[path[0]])).then(res1 => { |
|
||||||
|
|
||||||
if (Array.isArray(res1)) { |
|
||||||
return Promise.all(res1.map(r => extractNested(path.slice(1), r))) |
|
||||||
} else { |
|
||||||
return extractNested(path.slice(1), res1) |
|
||||||
} |
|
||||||
|
|
||||||
}) |
|
||||||
} else { |
|
||||||
return Promise.resolve(o) |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
// function for extracting field
|
|
||||||
function extractField(key) { |
|
||||||
if (!(ast && ast && ast[key] && ast[key].nested)) { |
|
||||||
if (resolverObj) { |
|
||||||
// resolve if it's resolver function
|
|
||||||
if (typeof resolverObj[key] === 'function') { |
|
||||||
res[key] = resolverObj[key]()//.call(res);
|
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} else if (typeof resolverObj[key] === 'object') { |
|
||||||
res[key] = Promise.resolve(resolverObj[key]) |
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} else { |
|
||||||
res[key] = Promise.resolve(resolverObj[key]) |
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} |
|
||||||
} |
|
||||||
} else { |
|
||||||
/* if (!res[ast[key].nested]) |
|
||||||
extractField(ast[key].nested.path) |
|
||||||
|
|
||||||
|
|
||||||
res[key] = res[ast.fields[key].nested.path].then(res => { |
|
||||||
if (typeof resolverObj[key] === 'function') { |
|
||||||
return resolverObj[key](res)//.call(res);
|
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} else if (typeof resolverObj[key] === 'object') { |
|
||||||
return Promise.resolve(resolverObj[key]) |
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} else { |
|
||||||
return Promise.resolve(resolverObj[key]) |
|
||||||
// console.log(prefix + res[key])
|
|
||||||
} |
|
||||||
})*/ |
|
||||||
// if nested extract the nested value
|
|
||||||
res[key] = extractNested(ast[key].nested.path, res, resolverObj) |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
for (const key of Object.keys(requestObj)) { |
|
||||||
// if (key in resolverObj) {
|
|
||||||
extractField(key); |
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
if (requestObj[key] && typeof requestObj[key] === 'object') { |
|
||||||
res[key] = res[key].then(res1 => { |
|
||||||
if (Array.isArray(res1)) { |
|
||||||
return Promise.all(res1.map(r => execute(requestObj[key], r, rootAst[ast[key].elementType] && rootAst[ast[key].elementType].fields))) |
|
||||||
} else { |
|
||||||
return execute(requestObj[key], res1, ast[key].fields) |
|
||||||
} |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
await Promise.all(Object.values(res)) |
|
||||||
|
|
||||||
const out = {}; |
|
||||||
for (const [k, v] of Object.entries(res)) { |
|
||||||
if(k in requestObj) |
|
||||||
out[k] = await v |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
return out |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
const req = { |
|
||||||
AddressList: { |
|
||||||
// City: {
|
|
||||||
// Country: 1
|
|
||||||
// },
|
|
||||||
Country: 1 |
|
||||||
}, |
|
||||||
CountryList: { |
|
||||||
// cityList: {
|
|
||||||
// addressCount: 1,
|
|
||||||
// addressList: {
|
|
||||||
// address: 1
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// c:1,
|
|
||||||
// d:1,
|
|
||||||
addressCount:1 |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
const extractNested = (path, o = {}) => { |
|
||||||
if (path.length) { |
|
||||||
if (!o[path[0]]) { |
|
||||||
o[path[0]] = Promise.resolve({}) |
|
||||||
} |
|
||||||
return o[path[0]].then(r => extractNested(path.slice(1), r)) |
|
||||||
} else { |
|
||||||
return Promise.resolve(o) |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
(async () => { |
|
||||||
|
|
||||||
console.time('start') |
|
||||||
console.log(JSON.stringify(await execute(req, nestResolver, rootAst), 0, 2)); |
|
||||||
console.timeEnd('start') |
|
||||||
|
|
||||||
|
|
||||||
/* console.log(JSON.stringify(await extractNested(['a', 'b'], { |
|
||||||
a: Promise.resolve({ |
|
||||||
b: Promise.resolve({ |
|
||||||
t: 1 |
|
||||||
}) |
|
||||||
}) |
|
||||||
}), 0, 2))*/ |
|
||||||
|
|
||||||
|
|
||||||
/* const o = {};*/ |
|
||||||
|
|
||||||
/* console.log(JSON.stringify(await extractNested(['a', 'b'], o), 0, 2)) |
|
||||||
console.log(o) |
|
||||||
console.log(JSON.stringify(await extractNested(['a', 'c'], o), 0, 2)) |
|
||||||
console.log(JSON.stringify(await extractNested(['a', 'b', 'c'], o), 0, 2)) |
|
||||||
console.log(JSON.stringify(await extractNested(['a', 'b'], o), 0, 2))*/ |
|
||||||
|
|
||||||
})().catch(e => console.log(e)).finally(() => process.exit(0)) |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,79 +0,0 @@ |
|||||||
GET http://localhost:8080/nc/{{projectId}}/api/v1/Film/v2 |
|
||||||
Accept: application/json |
|
||||||
Content-Type: application/json |
|
||||||
xc-auth: {{token}} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### |
|
||||||
|
|
||||||
GET http://localhost:8080/nc/{{projectId}}/api/v1/Actor/v2/1 |
|
||||||
Accept: application/json |
|
||||||
Content-Type: application/json |
|
||||||
xc-auth: {{token}} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GET http://localhost:8080/nc/{{projectId}}/api/v2/Country |
|
||||||
Accept: application/json |
|
||||||
Content-Type: application/json |
|
||||||
xc-auth: {{token}} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### |
|
||||||
GET http://localhost:8080/nc/{{projectId}}/api/v2/Address?f=test123 |
|
||||||
Accept: application/json |
|
||||||
Content-Type: application/json |
|
||||||
xc-auth: {{token}} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### |
|
||||||
|
|
||||||
POST http://localhost:8080/nc/{{projectId}}/generateLookup1 |
|
||||||
Accept: application/json |
|
||||||
Content-Type: application/json |
|
||||||
xc-auth: {{token}} |
|
||||||
|
|
||||||
|
|
||||||
{ |
|
||||||
|
|
||||||
"type" : "Lookup" |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### |
|
||||||
|
|
||||||
POST http://localhost:8080/nc/{{projectId}}/generate |
|
||||||
Accept: application/json |
|
||||||
Content-Type: application/json |
|
||||||
xc-auth: {{token}} |
|
||||||
|
|
||||||
|
|
||||||
{ |
|
||||||
"table": "Address", |
|
||||||
"type": "Formula", |
|
||||||
"formula": "CONCAT(CountryName,'____sdsdsdsd')", |
|
||||||
"alias": "test123" |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,17 +0,0 @@ |
|||||||
const a = { |
|
||||||
country:'abc', |
|
||||||
countryId:2 |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
a.__proto__ = { |
|
||||||
list(){ |
|
||||||
return this.countryId + 1 |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
console.log(a) |
|
||||||
console.log(a.list()) |
|
@ -1,334 +0,0 @@ |
|||||||
module.exports = [{ |
|
||||||
name: 'Simple', |
|
||||||
in: { |
|
||||||
CountryList: { |
|
||||||
country: 1 |
|
||||||
} |
|
||||||
}, |
|
||||||
out: { |
|
||||||
"CountryList": [ |
|
||||||
{ |
|
||||||
"country": "Afghanistan" |
|
||||||
}, |
|
||||||
{ |
|
||||||
"country": "Algeria" |
|
||||||
} |
|
||||||
] |
|
||||||
} |
|
||||||
},{ |
|
||||||
name: 'Nested', |
|
||||||
in: { |
|
||||||
CountryList: { |
|
||||||
country_id:1, |
|
||||||
country:1, |
|
||||||
cityList: { |
|
||||||
city:1, |
|
||||||
addressList:{ |
|
||||||
address:1 |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
|
||||||
out: { |
|
||||||
"CountryList": [ |
|
||||||
{ |
|
||||||
"country_id": 1, |
|
||||||
"country": "Afghanistan", |
|
||||||
"cityList": [ |
|
||||||
{ |
|
||||||
"city": "Kabul", |
|
||||||
"addressList": [ |
|
||||||
{ |
|
||||||
"address": "1168 Najafabad Parkway" |
|
||||||
} |
|
||||||
] |
|
||||||
} |
|
||||||
] |
|
||||||
}, |
|
||||||
{ |
|
||||||
"country_id": 2, |
|
||||||
"country": "Algeria", |
|
||||||
"cityList": [ |
|
||||||
{ |
|
||||||
"city": "Batna", |
|
||||||
"addressList": [ |
|
||||||
{ |
|
||||||
"address": "1924 Shimonoseki Drive" |
|
||||||
} |
|
||||||
] |
|
||||||
}, |
|
||||||
{ |
|
||||||
"city": "Bchar", |
|
||||||
"addressList": [ |
|
||||||
{ |
|
||||||
"address": "1031 Daugavpils Parkway" |
|
||||||
} |
|
||||||
] |
|
||||||
}, |
|
||||||
{ |
|
||||||
"city": "Skikda", |
|
||||||
"addressList": [ |
|
||||||
{ |
|
||||||
"address": "757 Rustenburg Avenue" |
|
||||||
} |
|
||||||
] |
|
||||||
} |
|
||||||
] |
|
||||||
} |
|
||||||
] |
|
||||||
} |
|
||||||
|
|
||||||
}, { |
|
||||||
name: 'Nested', |
|
||||||
in: { |
|
||||||
CountryList: { |
|
||||||
country: 1, |
|
||||||
cityList: { |
|
||||||
city: 1 |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
|
||||||
out: { |
|
||||||
"CountryList": [ |
|
||||||
{ |
|
||||||
"country": "Afghanistan", |
|
||||||
"cityList": [ |
|
||||||
{ |
|
||||||
"city": "Kabul" |
|
||||||
} |
|
||||||
] |
|
||||||
}, |
|
||||||
{ |
|
||||||
"country": "Algeria", |
|
||||||
"cityList": [ |
|
||||||
{ |
|
||||||
"city": "Batna" |
|
||||||
}, |
|
||||||
{ |
|
||||||
"city": "Bchar" |
|
||||||
}, |
|
||||||
{ |
|
||||||
"city": "Skikda" |
|
||||||
} |
|
||||||
] |
|
||||||
} |
|
||||||
] |
|
||||||
} |
|
||||||
}, { |
|
||||||
name: 'Lookup', |
|
||||||
in: { |
|
||||||
CountryList: { |
|
||||||
addressCount: 1 |
|
||||||
}, |
|
||||||
}, |
|
||||||
out: { |
|
||||||
"CountryList": [ |
|
||||||
{ |
|
||||||
"addressCount": [ |
|
||||||
1 |
|
||||||
] |
|
||||||
}, |
|
||||||
{ |
|
||||||
"addressCount": [ |
|
||||||
1, |
|
||||||
1, |
|
||||||
1 |
|
||||||
] |
|
||||||
} |
|
||||||
] |
|
||||||
} |
|
||||||
|
|
||||||
}, { |
|
||||||
name: 'Nested and Lookup', |
|
||||||
in: { |
|
||||||
CountryList: { |
|
||||||
cityList: { |
|
||||||
city: 1 |
|
||||||
}, |
|
||||||
addressCount: 1 |
|
||||||
}, |
|
||||||
}, |
|
||||||
out: { |
|
||||||
"CountryList": [ |
|
||||||
{ |
|
||||||
"cityList": [ |
|
||||||
{ |
|
||||||
"city": "Kabul" |
|
||||||
} |
|
||||||
], |
|
||||||
"addressCount": [ |
|
||||||
1 |
|
||||||
] |
|
||||||
}, |
|
||||||
{ |
|
||||||
"cityList": [ |
|
||||||
{ |
|
||||||
"city": "Batna" |
|
||||||
}, |
|
||||||
{ |
|
||||||
"city": "Bchar" |
|
||||||
}, |
|
||||||
{ |
|
||||||
"city": "Skikda" |
|
||||||
} |
|
||||||
], |
|
||||||
"addressCount": [ |
|
||||||
1, |
|
||||||
1, |
|
||||||
1 |
|
||||||
] |
|
||||||
} |
|
||||||
] |
|
||||||
} |
|
||||||
}, { |
|
||||||
name: 'Deeply nested Lookup', |
|
||||||
in: { |
|
||||||
CountryList: { |
|
||||||
addressList: 1 |
|
||||||
}, |
|
||||||
}, |
|
||||||
out: { |
|
||||||
"CountryList": [ |
|
||||||
{ |
|
||||||
"addressList": [ |
|
||||||
"1168 Najafabad Parkway" |
|
||||||
] |
|
||||||
}, |
|
||||||
{ |
|
||||||
"addressList": [ |
|
||||||
"1924 Shimonoseki Drive", |
|
||||||
"1031 Daugavpils Parkway", |
|
||||||
"757 Rustenburg Avenue" |
|
||||||
] |
|
||||||
} |
|
||||||
] |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
name: 'Belongs relation', |
|
||||||
in: { |
|
||||||
AddressList: { |
|
||||||
address: 1, |
|
||||||
City: { |
|
||||||
city: 1 |
|
||||||
} |
|
||||||
}, |
|
||||||
}, |
|
||||||
out: { |
|
||||||
"AddressList": [ |
|
||||||
{ |
|
||||||
"address": "47 MySakila Drive", |
|
||||||
"City": { |
|
||||||
"city": "Lethbridge" |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
"address": "28 MySQL Boulevard", |
|
||||||
"City": { |
|
||||||
"city": "Woodridge" |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
"address": "23 Workhaven Lane", |
|
||||||
"City": { |
|
||||||
"city": "Lethbridge" |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
"address": "1411 Lillydale Drive", |
|
||||||
"City": { |
|
||||||
"city": "Woodridge" |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
"address": "1913 Hanoi Way", |
|
||||||
"City": { |
|
||||||
"city": "Sasebo" |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
"address": "1121 Loja Avenue", |
|
||||||
"City": { |
|
||||||
"city": "San Bernardino" |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
"address": "692 Joliet Street", |
|
||||||
"City": { |
|
||||||
"city": "Athenai" |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
"address": "1566 Inegl Manor", |
|
||||||
"City": { |
|
||||||
"city": "Myingyan" |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
"address": "53 Idfu Parkway", |
|
||||||
"City": { |
|
||||||
"city": "Nantou" |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
"address": "1795 Santiago de Compostela Way", |
|
||||||
"City": { |
|
||||||
"city": "Laredo" |
|
||||||
} |
|
||||||
} |
|
||||||
] |
|
||||||
} |
|
||||||
}, { |
|
||||||
name: 'Belongs relation with deep lookup', |
|
||||||
in: { |
|
||||||
AddressList: { |
|
||||||
address: 1, |
|
||||||
CountryName:1 |
|
||||||
}, |
|
||||||
}, |
|
||||||
out: { |
|
||||||
"AddressList": [ |
|
||||||
{ |
|
||||||
"address": "47 MySakila Drive", |
|
||||||
"CountryName": "Canada" |
|
||||||
}, |
|
||||||
{ |
|
||||||
"address": "28 MySQL Boulevard", |
|
||||||
"CountryName": "Australia" |
|
||||||
}, |
|
||||||
{ |
|
||||||
"address": "23 Workhaven Lane", |
|
||||||
"CountryName": "Canada" |
|
||||||
}, |
|
||||||
{ |
|
||||||
"address": "1411 Lillydale Drive", |
|
||||||
"CountryName": "Australia" |
|
||||||
}, |
|
||||||
{ |
|
||||||
"address": "1913 Hanoi Way", |
|
||||||
"CountryName": "Japan" |
|
||||||
}, |
|
||||||
{ |
|
||||||
"address": "1121 Loja Avenue", |
|
||||||
"CountryName": "United States" |
|
||||||
}, |
|
||||||
{ |
|
||||||
"address": "692 Joliet Street", |
|
||||||
"CountryName": "Greece" |
|
||||||
}, |
|
||||||
{ |
|
||||||
"address": "1566 Inegl Manor", |
|
||||||
"CountryName": "Myanmar" |
|
||||||
}, |
|
||||||
{ |
|
||||||
"address": "53 Idfu Parkway", |
|
||||||
"CountryName": "Taiwan" |
|
||||||
}, |
|
||||||
{ |
|
||||||
"address": "1795 Santiago de Compostela Way", |
|
||||||
"CountryName": "United States" |
|
||||||
} |
|
||||||
] |
|
||||||
} |
|
||||||
} |
|
||||||
] |
|
Loading…
Reference in new issue