From f4dd51ac31ed9f4a903e7e14b5753370e5f04746 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Tue, 30 Mar 2021 09:07:02 +1100 Subject: [PATCH] Working on test framework --- README.md | 3 +- source/build-type-graph.js | 12 ++-- source/keywords.json | 2 +- test/index.js | 125 +++++++++++++++++++++++++++++++++++-- 4 files changed, 129 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index c0f3595..5acda38 100644 --- a/README.md +++ b/README.md @@ -138,8 +138,9 @@ The fields can be altered as well, using the query language's built-in keywords: * delete * match * set +* typeName -`create`, `update` and `delete` work as expected. +`create`, `update` and `delete` are still to be defined properly, but they'll probably work like this: ### Create diff --git a/source/build-type-graph.js b/source/build-type-graph.js index 6a516cf..c6061e8 100644 --- a/source/build-type-graph.js +++ b/source/build-type-graph.js @@ -7,10 +7,10 @@ const parseInput = require('./parse-input'); const buildTypeGraph = (schema, options) => { //the default graph let graph = { - String: { scalar: true }, - Integer: { scalar: true }, - Float: { scalar: true }, - Boolean: { scalar: true }, + String: { typeName: 'String', scalar: true }, + Integer: { typeName: 'Integer', scalar: true }, + Float: { typeName: 'Float', scalar: true }, + Boolean: { typeName: 'Boolean', scalar: true }, }; //parse the schema @@ -35,7 +35,7 @@ const buildTypeGraph = (schema, options) => { throw 'Unexpected keyword ' + tokens[pos]; } - graph[tokens[pos++]] = { scalar: true }; + graph[tokens[pos++]] = { typeName: tokens[pos - 1], scalar: true }; if (options.debug) { console.log(`Defined ${tokens[pos - 1]}:\n`, graph[tokens[pos - 1]]); @@ -63,7 +63,7 @@ const parseCompoundType = (tokens, pos, scalars, options) => { } //graph component to be returned - const compound = {}; + const compound = { typeName: tokens[pos - 1] }; //for each line of the compound type while (tokens[pos++] && tokens[pos] !== '}') { diff --git a/source/keywords.json b/source/keywords.json index 4219b60..9a59b40 100644 --- a/source/keywords.json +++ b/source/keywords.json @@ -1 +1 @@ -["type", "scalar", "create", "update", "delete", "set", "match"] \ No newline at end of file +["type", "scalar", "create", "update", "delete", "set", "match", "typeName"] \ No newline at end of file diff --git a/test/index.js b/test/index.js index 795dc04..d169910 100644 --- a/test/index.js +++ b/test/index.js @@ -1,7 +1,122 @@ -//the library to test -const sineQL = require('../source/index.js'); +//mock tools +const books = { + findAll: async args => { + let arr = [ + { title: 'The Wind in the Willows', published: '1908-06-15' } + ]; -//the dummy values + const { attributes, where } = args; + + arr = arr.filter(el => !where || el.title == where.title || el.published == where.published); //TODO: fix this + + return arr; + } +} + +const authors = { + findAll: async args => { + const arr = [ + { name: 'Kenneth Grahame', bookIds: [1] }, + ]; + + const { attributes, where } = args; + + arr = arr.filter(el => !where || el.title == where.title || el.published == where.published); //TODO: fix this + + return arr; + } +} + +//the handler functions return arrays for each type, containing every element that satisfies the queries + +//the "query" argument contains the object built from the sineQL query +//the "graph" argument contains the typeGraph + +//the task of the handler functions is to query the database, and return the correct results + +/* possible values for "query" include: + +{ + typeName: 'Author', + name: { typeName: 'String', scalar: true, match: 'Kenneth Grahame' }, + books: { typeName: 'Book', match: { + typeName: 'Book', + title: { typeName: 'String', scalar: true, match: 'The wind in the Willows' } + published: { typeName: 'Date', scalar: true } + } +} + +*/ + +//depth-first search seems to be the best option +//Each query shouldn't know if it's a sub-query + +const handler = { + //complex compound + Author: async (query, graph) => { + //get the fields alone + const { typeName, ...fields } = query; + + //get the names of matched fields + const matchedNames = Object.keys(fields.filter(field => field.match)); + + //short-circuit if querying everything + const where = {}; + if (matchedNames.length > 0) { + //build the "where" object + matchedNames.forEach(mn => { + where[mn] = { + [Op.eq]: query[mn].match + } + }); + } + + //these are field names + const scalars = Object.keys(fields).filter(field => graph[field.typeName].scalar); + const nonScalars = Object.keys(fields).filter(field => !graph[field.typeName].scalar); + + const results = await authors.findAll({ + attributes: scalars, //fields to find (keys) + where: where + }); //sequelize ORM model + + nonScalars.forEach(nonScalar => { + //delegate to a deeper part of the tree + results[nonScalar] = handler[fields[nonScalar].typeName](fields[nonScalar], graph); + }); + + //finally, return the results + return results; + }, + + //simple compound + Book: async (query, graph) => { + //get the fields alone + const { typeName, ...fields } = query; + + //get the names of matched fields + const matchedNames = Object.keys(fields.filter(field => field.match)); + + //short-circuit if querying everything + const where = {}; + if (matchedNames.length > 0) { + //build the "where" object + matchedNames.forEach(mn => { + where[mn] = { + [Op.eq]: query[mn].match + } + }); + } + + //return the result + return await books.findAll({ + attributes: Object.keys(fields), //fields to find + where: where + }); //sequelize ORM model + } +}; + +//the matching schema const schema = ` scalar Date @@ -16,8 +131,8 @@ type Author { } `; -const handler = null; +//the library to test +const sineQL = require('../source/index.js'); //run the function in debug mode (builds type graph) const sine = sineQL(schema, handler, { debug: true }); -