|
@@ -67,7 +67,7 @@ async function createModels(gql, config={create: 'Upsert', update: 'Upsert', del
|
|
|
let classes = {}
|
|
|
|
|
|
const createClass = (name, fields) => {
|
|
|
- if (!(name in classes))
|
|
|
+ if (!(name in classes)) {
|
|
|
classes[name] = class {
|
|
|
constructor(data){
|
|
|
Object.assign(this, data)
|
|
@@ -77,6 +77,8 @@ async function createModels(gql, config={create: 'Upsert', update: 'Upsert', del
|
|
|
return fields
|
|
|
}
|
|
|
}
|
|
|
+ Object.defineProperty(classes[name], 'name', {value: name})
|
|
|
+ }
|
|
|
return classes[name]
|
|
|
}
|
|
|
|
|
@@ -129,6 +131,233 @@ async function createModels(gql, config={create: 'Upsert', update: 'Upsert', del
|
|
|
return classes
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+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
|
|
|
+ }
|
|
|
+
|
|
|
+ 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){
|
|
|
+ const gqlQuery = `
|
|
|
+ query ${name}Find($query: String){
|
|
|
+ ${name}Find(query: $query)
|
|
|
+ ${projectionBuilder(type)}
|
|
|
+ }
|
|
|
+ `
|
|
|
+ let result = await gql.request(gqlQuery, {query: JSON.stringify(query)})
|
|
|
+ return result[name + 'Find'].map(entity => new classes[name](entity))
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ 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`)))
|
|
|
+ console.log(classes)
|
|
|
+ classes.Good.find([{}]).then(goods => console.log(goods))
|
|
|
+}
|
|
|
+
|
|
|
const dataReader = async () => {
|
|
|
const { PriceItem } = await createModels(gql)
|
|
|
let data = await PriceItem.find({}, {limit: [10]})
|
|
@@ -159,15 +388,16 @@ function ModelGrid({model, rowHeight, gridHeight, overload, query, cellDoubleCli
|
|
|
const jsonQuery = JSON.stringify(query)
|
|
|
|
|
|
const [totalCount, setTotalCount] = useState()
|
|
|
+
|
|
|
if (!totalCount || oldQuery !== jsonQuery) {
|
|
|
- debugger;
|
|
|
- model.count(query).then(count => {debugger; setTotalCount(count); setOldQuery(jsonQuery)})
|
|
|
+ model && model.count(query).then(count => { setTotalCount(count); setOldQuery(jsonQuery)})
|
|
|
}
|
|
|
|
|
|
const [gridContentRef, setGridContentRef] = useState()
|
|
|
const [scroll, setScroll] = useState(0)
|
|
|
|
|
|
|
|
|
+
|
|
|
const onScreenFirstRowIndex = Math.floor(scroll/rowHeight)
|
|
|
let skip = onScreenFirstRowIndex - (overloadedRowCount - onScreenRowCount)/2;
|
|
|
|
|
@@ -178,10 +408,12 @@ function ModelGrid({model, rowHeight, gridHeight, overload, query, cellDoubleCli
|
|
|
const [records, setRecords] = useState()
|
|
|
const cursorCalls = {skip: [skip], limit: [overloadedRowCount]}
|
|
|
|
|
|
+
|
|
|
+ if (!model) return <>No model</>;
|
|
|
+
|
|
|
if (sort.length) cursorCalls.sort = [sort]
|
|
|
|
|
|
if (!records || oldQuery !== jsonQuery) {
|
|
|
- debugger;
|
|
|
model.find(query, cursorCalls)
|
|
|
.then(records => (setRecords(records), setOldQuery(jsonQuery)))
|
|
|
}
|
|
@@ -220,13 +452,15 @@ function App() {
|
|
|
let [models, setModels] = useState()
|
|
|
let [queryText, setQueryText] = useState('{manufacturerName: "TOYOTA"}')
|
|
|
let [query, setQuery] = useState({})
|
|
|
- models || createModels(gql).then(models => setModels(models))
|
|
|
+ models || createModels2(gql).then(models => setModels(models))
|
|
|
|
|
|
console.log(query)
|
|
|
+ console.log('MODELS', models)
|
|
|
|
|
|
return (
|
|
|
<div className="App">
|
|
|
- {models && <ModelGrid model={models.PriceItem} rowHeight={50} gridHeight={700} overload={5} query={query}
|
|
|
+ {models && Object.entries(models).map(([key, model]) => <div>{key}</div>)}
|
|
|
+ {models && <ModelGrid model={models.jk} rowHeight={50} gridHeight={700} overload={5} query={query}
|
|
|
cellDoubleClick={(name, text, _id) => setQueryText(`{${name}:\`${text}\`}`)}/> }
|
|
|
<textarea onChange={e => setQueryText(e.target.value)} value={queryText} />
|
|
|
<button onClick={() => setQuery(eval(`(${queryText})`))}>Run</button>
|