From 7fa4c56bccd7c7f9928df7a949bced5e29a9b427 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Sat, 5 Sep 2020 00:05:56 +1000 Subject: [PATCH] Worked on the schema code --- package.json | 2 +- server/database.js | 4 +-- server/handler.js | 4 +-- server/schema.js | 6 ++--- server/server.js | 3 ++- server/simpleQL/index.js | 55 ++++++++++++++++++++++++---------------- 6 files changed, 43 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index 2e27e63..4e586b5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "simpleql", - "version": "1.0.0", + "version": "0.1.0", "description": "", "main": "server/server.js", "directories": { diff --git a/server/database.js b/server/database.js index fd27910..1f68e22 100644 --- a/server/database.js +++ b/server/database.js @@ -21,7 +21,7 @@ let authors = [ }, ]; -//insert the authors into the books +//insert the authors into the books (relationship) authors = authors.map(a => { a.books = a.books.map(b => { b.author = a; @@ -39,4 +39,4 @@ authors.forEach(a => books = books.concat(a.books)); module.exports = { authors, books, -}; \ No newline at end of file +}; diff --git a/server/handler.js b/server/handler.js index f44dc77..5d23963 100644 --- a/server/handler.js +++ b/server/handler.js @@ -11,7 +11,7 @@ const database = require('./database.js'); //the handler routines const handler = { Book: (parent, scalars) => { - //takes an object which is the result of the parent query, if there is one { typeName: 'Author', scalars: [scalars], context: parentObject } + //takes an object which is the result of the parent query, if there is one { typeName: 'Author', scalars: [scalars] } //takes an array of scalar types as objects: { typeName: 'String', name: 'title' } //must return an array of objects containing the results @@ -82,4 +82,4 @@ const handler = { }, }; -module.exports = handler; \ No newline at end of file +module.exports = handler; diff --git a/server/schema.js b/server/schema.js index 5daab9a..41ad1d7 100644 --- a/server/schema.js +++ b/server/schema.js @@ -2,13 +2,13 @@ module.exports = ` scalar Date type Book { - String title + !String title Author author Date published } type Author { !String name - !Book[] books + Book[] books } -`; \ No newline at end of file +`; diff --git a/server/server.js b/server/server.js index 0b5e5fe..2a8d9a1 100644 --- a/server/server.js +++ b/server/server.js @@ -2,6 +2,7 @@ const express = require('express'); const bodyParser = require('body-parser'); const app = express(); + app.use(bodyParser.text()); //test the library @@ -20,4 +21,4 @@ app.post('/simpleql', async (req, res) => { //startup app.listen(process.env.WEB_PORT || 3100, err => { console.log(`listening to *:${process.env.WEB_PORT || 3100}`); -}); \ No newline at end of file +}); diff --git a/server/simpleQL/index.js b/server/simpleQL/index.js index 4bca894..2659740 100644 --- a/server/simpleQL/index.js +++ b/server/simpleQL/index.js @@ -1,13 +1,24 @@ +//reserved keywords that can't be used as identifiers +const keywords = ['type', 'scalar']; + //the main function to be returned const main = (schema, handler) => { - const typeGraph = buildTypeGraph(schema); + let typeGraph; + + try { + typeGraph = buildTypeGraph(schema); + } + catch(e) { + console.log('caught in typegraph', e); + return null; + } console.log(typeGraph); //the receiving function - this will be called multiple times return async reqBody => { //parse the query - const tokens = reqBody.split(/(\s+)/).filter(s => s.trim().length > 0); + const tokens = reqBody.split(/(\s+)/).filter(s => s.trim().length > 0); //TODO: proper token parsing let pos = 0; try { @@ -17,12 +28,12 @@ const main = (schema, handler) => { case 'update': case 'delete': throw 'keyword not implemented: ' + tokens[pos]; - //TODO + //TODO: implement these keywords break; //no leading keyword - regular query default: - const result = await parseQuery(handler, tokens, pos, typeGraph[tokens[pos]], typeGraph); + const result = await parseQuery(handler, tokens, pos, typeGraph); return [200, result]; @@ -47,7 +58,7 @@ const buildTypeGraph = schema => { }; //parse the schema - const tokens = schema.split(/(\s+)/).filter(s => s.trim().length > 0); + const tokens = schema.split(/(\s+)/).filter(s => s.trim().length > 0); //TODO: proper token parsing let pos = 0; while (tokens[pos]) { @@ -57,12 +68,12 @@ const buildTypeGraph = schema => { graph[tokens[pos]] = parseCompoundType(tokens, pos); //advance to the end of the compound type - while(tokens[pos] && tokens[pos++] != '}'); + pos = eatBlock(tokens, pos + 2); //+2: skip the name & opening bracket break; case 'scalar': - if (['type', 'scalar'].includes(graph[tokens[pos]])) { + if (keywords.includes(graph[tokens[pos]])) { throw 'Unexpected keyword ' + graph[tokens[pos]]; } @@ -95,11 +106,11 @@ const parseCompoundType = (tokens, pos) => { //parse the extra typing data let array = false; - let nullable = true; + let required = false; //not nullable if (type[0] === '!') { - nullable = false; + required = true; type = type.slice(1); } @@ -114,32 +125,32 @@ const parseCompoundType = (tokens, pos) => { checkAlphaNumeric(name); //can't use keywords - if (['type', 'scalar'].includes(type) || ['type', 'scalar'].includes(name)) { - throw 'Unexpected keyword found as type field or type name'; + if (keywords.includes(type) || keywords.includes(name)) { + throw 'Unexpected keyword found as type field or type name (' + type + ' ' + name + ')'; } //check for duplicate fields if (Object.keys(compound).includes(name)) { - throw 'Unexpected duplicate filed name'; + throw 'Unexpected duplicate field name'; } //finally, push to the compound definition compound[name] = { typeName: type, array: array, - nullable: nullable, + required: required, }; } return compound; }; -const parseQuery = async (handler, tokens, pos, typeGraph, superTypeGraph, parent = null) => { +const parseQuery = async (handler, tokens, pos, typeGraph, parent = null) => { //returns an object result from handler //get the "parent object" contents for sub-objects - const queryName = superTypeGraph[tokens[pos]] ? null : tokens[pos]; //if you're a type, name = null - const queryType = superTypeGraph[tokens[pos]] ? tokens[pos] : superTypeGraph[parent.typeName][tokens[pos]].typeName; //use this type or derive the type from the parent +// const queryName = typeGraph[tokens[pos]] ? null : tokens[pos]; //if you're a type, name = null +// const queryType = typeGraph[tokens[pos]] ? tokens[pos] : typeGraph[parent.typeName][tokens[pos]].typeName; //use this type or derive the type from the parent //move on pos++; @@ -165,7 +176,7 @@ const parseQuery = async (handler, tokens, pos, typeGraph, superTypeGraph, paren } //type is a scalar, and can be queried - if (superTypeGraph[typeGraph[tokens[pos]].typeName].scalar) { + if (typeGraph[typeGraph[tokens[pos]].typeName].scalar) { //push the scalar object to the queryFields scalarFields.push({ typeName: typeGraph[tokens[pos]].typeName, name: tokens[pos] }); @@ -178,9 +189,8 @@ const parseQuery = async (handler, tokens, pos, typeGraph, superTypeGraph, paren handler, tokens, pos2, - superTypeGraph[typeGraph[tokens[pos2]].typeName], - superTypeGraph, - { typeName: queryType, scalars: scalarFields, context: result } + typeGraph, + { typeName: queryType, scalars: scalarFields } //parent object )]); pos = eatBlock(tokens, pos); @@ -192,6 +202,7 @@ const parseQuery = async (handler, tokens, pos, typeGraph, superTypeGraph, paren let results = handler[queryType](parent, scalarFields); + //WTF results = await Promise.all(results.map(async res => { const tuples = await Promise.all(deferredCalls.map(async call => await call(res))); @@ -219,8 +230,8 @@ const eatBlock = (tokens, pos) => { } } - return pos; + return ++pos; }; //return -module.exports = main; \ No newline at end of file +module.exports = main;