diff --git a/package.json b/package.json index f06c8ab..ac529ca 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "description": "Hasura object mapping", "main": "src/index.js", "scripts": { - "test": "nyc --reporter=html --reporter=lcovonly ava", - "test_dev": "nyc --reporter=html --reporter=text ava --watch", + "test": "nyc --reporter=html --reporter=lcovonly ava --verbose", + "test_dev": "nyc --reporter=html --reporter=text ava --watch --verbose", "release": "npm-github-release", "coverage": "nyc report --reporter=text-lcov | coveralls" }, @@ -17,7 +17,9 @@ ], "require": [ "esm" - ] + ], + "failWithoutAssertions": false, + "concurrency": 16 }, "husky": { "hooks": { diff --git a/src/gql/fragment.js b/src/gql/fragment.js index 536531e..3b99627 100644 --- a/src/gql/fragment.js +++ b/src/gql/fragment.js @@ -19,9 +19,9 @@ class Fragment { return this._gqlFields; } - toString() { + build() { let fragmentName = `${this.params.name}_fragment_${this.params.table}`; - let fields = this._gqlFields; + let fields = this.gqlFields(); return { name: fragmentName, diff --git a/src/index.js b/src/index.js index c336082..49ad478 100644 --- a/src/index.js +++ b/src/index.js @@ -37,7 +37,7 @@ class Hasura { if (err) throw err; data.forEach((row) => { - this.tables[row.table_name] = new Table({ + this.createTable({ name: row.table_name, type: row.table_type, }); @@ -52,7 +52,7 @@ class Hasura { if (err) throw err; data.forEach((row) => { - this.tables[row.table_name].setField({ + this.table(row.table_name).setField({ name: row.column_name, type: row.data_type, udt: row.udt_name, @@ -77,14 +77,14 @@ class Hasura { if (err) throw err; data.forEach((row) => { - this.tables[row.table_name].setPrimarykey({ + this.table(row.table_name).setPrimarykey({ name: row.key_column, position: row.position, }); }); Object.keys(this.tables).forEach((tableName) => { - this.tables[tableName].init(); + this.table(tableName).init(); }); this.INITIATED = true; @@ -96,6 +96,10 @@ class Hasura { return this.tables[name]; } + createTable({ name, type } = {}) { + this.tables[name] = new Table({ name, type }); + } + /* { [table_name]: { diff --git a/src/table.js b/src/table.js index fcf9cfd..90d081a 100644 --- a/src/table.js +++ b/src/table.js @@ -38,21 +38,24 @@ class Table { } init() { - this.createFragment(); + this.createFragment('base'); } - fragment(name) { + fragment(name = 'base') { if (typeof this.fragments[name] == 'undefined') throw new Error(`fragment ${name} not found`); return this.fragments[name]; } createFragment(name = 'base', fields = false) { + if (Object.keys(this.fields).length == 0 && !fields) return false; + this.fragments[name] = new Fragment({ table: this.params.name, name, fields: fields ? fields : this.fields, }); + return this.fragments[name]; } /* @@ -325,29 +328,17 @@ class Table { fragmentName = ''; let fields = params.fields ? fieldsToGql(params.fields) : false; if (!fields) { - if (params.fragment) { - if (params.fragment instanceof Fragment) { - let fragmentObject = params.fragment.toString(); - fragment = fragmentObject.raw; - fragmentName = fragmentObject.name; - fields = `...${fragmentObject.name}`; - } else if (typeof this.fragments[params.fragment] != 'undefined') { - let fragmentObject = this.fragments[params.fragment].toString(); - fragment = fragmentObject.raw; - fragmentName = fragmentObject.name; - fields = `...${fragmentObject.name}`; - } else { - let fragmentObject = this.fragments.base.toString(); - fragment = fragmentObject.raw; - fragmentName = fragmentObject.name; - fields = `...${fragmentObject.name}`; - } - } else { - let fragmentObject = this.fragments.base.toString(); - fragment = fragmentObject.raw; - fragmentName = fragmentObject.name; - fields = `...${fragmentObject.name}`; - } + let fInstance = null; + if (typeof params.fragment == 'string') fInstance = this.fragment(params.fragment); + else if (params.fragment instanceof Fragment) fInstance = params.fragment; + else fInstance = this.fragment('base'); + + if (!fInstance) throw new Error('table do not contain any fragment'); + + let fragmentObject = fInstance.build(); + fragment = fragmentObject.raw; + fragmentName = fragmentObject.name; + fields = `...${fragmentObject.name}`; } if (!fields) throw new Error('no returning fields specified'); diff --git a/tests/fragment.js b/tests/fragment.js index 982fd1a..7367894 100644 --- a/tests/fragment.js +++ b/tests/fragment.js @@ -25,12 +25,12 @@ test('getting fragment name', (t) => { }); //right naming using table and fragment names - var { name } = fragment.toString(); + var { name } = fragment.build(); t.is(name, 'base_fragment_test'); fragment.params.table = 'test2'; fragment.params.name = 'new'; - var { name } = fragment.toString(); + var { name } = fragment.build(); t.is(name, 'new_fragment_test2'); }); @@ -57,7 +57,7 @@ test('checking fragment decalration', (t) => { }, ], }); - var { raw } = fragment.toString(); + var { raw } = fragment.build(); t.deepEqual(gql(raw), testFragment); //declaring fileds with string @@ -71,7 +71,7 @@ test('checking fragment decalration', (t) => { } `, }); - var { raw } = fragment.toString(); + var { raw } = fragment.build(); t.deepEqual(gql(raw), testFragment); //declaring fileds with object @@ -85,7 +85,7 @@ test('checking fragment decalration', (t) => { }, }, }); - var { raw } = fragment.toString(); + var { raw } = fragment.build(); t.deepEqual(gql(raw), testFragment); }); @@ -143,7 +143,7 @@ test('checking big declaration', (t) => { }, ], }); - var { raw } = fragment.toString(); + var { raw } = fragment.build(); t.deepEqual(gql(raw), testFragment); }); @@ -208,3 +208,64 @@ test('check for incopatable fields format', (t) => { { instanceOf: Error }, ); }); + +test('check extension', (t) => { + const testFragment = gql` + fragment main_fragment_test on test { + id + name + logo { + host + path + } + } + `; + + let baseTestFragment = new Fragment({ + name: 'base', + table: 'test', + fields: ['id', 'name'], + }); + let baseLogoFragment = new Fragment({ + name: 'base', + table: 'logo', + fields: ['host', 'path'], + }); + + var mainFragment = new Fragment({ + name: 'main', + table: 'test', + fields: [ + baseTestFragment.gqlFields(), + { + key: 'logo', + values: baseLogoFragment.gqlFields(), + }, + ], + }); + var { raw } = mainFragment.build(); + t.deepEqual(gql(raw), testFragment); + + var mainFragment = new Fragment({ + name: 'main', + table: 'test', + fields: ` + ${baseTestFragment.gqlFields()} + logo { + ${baseLogoFragment.gqlFields()} + } + `, + }); + var { raw } = mainFragment.build(); + t.deepEqual(gql(raw), testFragment); +}); + +test('check gqlFields function', (t) => { + let baseTestFragment = new Fragment({ + name: 'base', + table: 'test', + fields: ['id', 'name'], + }); + + t.is(typeof baseTestFragment.gqlFields(), 'string'); +}); diff --git a/tests/hasura.js b/tests/hasura.js index 064c483..1e0ef78 100644 --- a/tests/hasura.js +++ b/tests/hasura.js @@ -1,6 +1,6 @@ require('dotenv').config(); const test = require('ava'); -const { Hasura } = require('../src/'); +const { Hasura, Table } = require('../src/'); test('throws without params', (t) => { t.throws( @@ -9,9 +9,7 @@ test('throws without params', (t) => { }, { instanceOf: Error }, ); -}); -test('still throws without params', (t) => { t.throws( () => { const orm = new Hasura({ @@ -20,9 +18,7 @@ test('still throws without params', (t) => { }, { instanceOf: Error }, ); -}); -test('graphqlUrl is not an url', (t) => { t.throws( () => { const orm = new Hasura({ @@ -32,3 +28,39 @@ test('graphqlUrl is not an url', (t) => { { instanceOf: Error }, ); }); + +test('succesful contructor', (t) => { + let orm = new Hasura({ + graphqlUrl: 'efwehfwiefjwopeif', + adminSecret: 'qwdqwdqwdqwd', + }); + + t.true(orm instanceof Hasura); +}); + +test('getting table', (t) => { + let orm = new Hasura({ + graphqlUrl: 'efwehfwiefjwopeif', + adminSecret: 'qwdqwdqwdqwd', + }); + + t.throws( + () => { + orm.table('do_not_exist'); + }, + { instanceOf: Error }, + ); + + t.throws( + () => { + orm.createTable(); + }, + { instanceOf: Error }, + ); + + orm.createTable({ + name: 'test', + type: 'BASE TABLE', + }); + t.true(orm.table('test') instanceof Table); +}); diff --git a/tests/table.js b/tests/table.js index e169f23..dc7b7b3 100644 --- a/tests/table.js +++ b/tests/table.js @@ -1,6 +1,8 @@ require('dotenv').config(); const test = require('ava'); -const { Table } = require('../src/'); +const { Table, Fragment } = require('../src/'); +import gql from 'graphql-tag'; +gql.disableFragmentWarnings(); test('throws without params', (t) => { t.throws( @@ -9,9 +11,7 @@ test('throws without params', (t) => { }, { instanceOf: Error }, ); -}); -test('still throws without params', (t) => { t.throws( () => { const orm = new Table({ @@ -72,3 +72,140 @@ test('field not found', (t) => { { instanceOf: Error }, ); }); + +test('fragment not found', (t) => { + let table = new Table({ + name: 'test', + type: 'BASE TABLE', + }); + + t.throws( + () => { + table.fragment('test'); + }, + { instanceOf: Error }, + ); + + /* + Note! Will not create fragment without fields + */ + table.createFragment('base'); + t.throws( + () => { + table.fragment('test'); + }, + { instanceOf: Error }, + ); +}); + +test('fragment found', (t) => { + let table = new Table({ + name: 'test', + type: 'BASE TABLE', + }); + table.setField({ + name: 'id', + type: 'Integer', + }); + table.createFragment('base'); + + t.true(table.fragment('base') instanceof Fragment); + + const testFragment = gql` + fragment base_fragment_test on test { + id + } + `; + var { raw } = table.fragment('base').build(); + t.deepEqual(gql(raw), testFragment); +}); + +test('build fields for query', (t) => { + let table = new Table({ + name: 'test', + type: 'BASE TABLE', + }); + table.setField({ + name: 'id', + type: 'Integer', + }); + + //no fragments + t.throws( + () => { + table.getFieldsFromParams({}); + }, + { instanceOf: Error }, + ); + + table.init(); + + /* + When no input specified, we use base fragment + */ + var testFragment = gql` + fragment base_fragment_test on test { + id + } + `; + var { fields, fragment, fragmentName } = table.getFieldsFromParams({}); + t.is(fragmentName, 'base_fragment_test'); + t.deepEqual(gql(fragment), testFragment); + t.is(fields, '...base_fragment_test'); + + var { fields, fragment, fragmentName } = table.getFieldsFromParams({ + fragment: 'base', + }); + t.is(fragmentName, 'base_fragment_test'); + t.deepEqual(gql(fragment), testFragment); + t.is(fields, '...base_fragment_test'); + + /* + Will throw an error when can't find a fragment in table + */ + t.throws( + () => { + table.getFieldsFromParams({ + fragment: 'fragment_do_not_exist', + }); + }, + { instanceOf: Error }, + ); + + /* + When no input specified, we use base fragment + */ + var { fields, fragment, fragmentName } = table.getFieldsFromParams({ + fields: ` + id + logo + `, + }); + t.is(fragmentName, ''); + t.is(fragment, ''); + t.true(fields.indexOf('id') != -1); + t.true(fields.indexOf('logo') != -1); + + /* + We passing custom fragment + */ + var testFragment = gql` + fragment main_fragment_test on test { + id + test + } + `; + var { fields, fragment, fragmentName } = table.getFieldsFromParams({ + fragment: new Fragment({ + name: 'main', + table: 'test', + fields: ` + id + test + `, + }), + }); + t.is(fragmentName, 'main_fragment_test'); + t.deepEqual(gql(fragment), testFragment); + t.is(fields, '...main_fragment_test'); +});