Skip to content
This repository has been archived by the owner on Apr 28, 2023. It is now read-only.

fix: JSONB column values no longer stringified #8

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
24 changes: 24 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
version: "3.6"
services:
postgres:
image: postgres
restart: always
volumes:
- ../hasura/init:/docker-entrypoint-initdb.d/
- db_data:/var/lib/postgresql/data
graphql-engine:
image: hasura/graphql-engine:v1.0.0-beta.8.cli-migrations
ports:
- "8080:8080"
depends_on:
- "postgres"
restart: always
environment:
HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:@postgres:5432/postgres
HASURA_GRAPHQL_ENABLE_CONSOLE: "true" # set to "false" to disable console
HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log
# HASURA_GRAPHQL_MIGRATIONS_DIR: "/migrations"
## uncomment next line to set an admin secret
HASURA_GRAPHQL_ADMIN_SECRET: myadminsecretkey
volumes:
db_data:
23 changes: 11 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
"@oclif/errors": "1.1.2",
"@oclif/plugin-help": "2.0.5",
"cli-ux": "4.7.3",
"glob": "^7.1.5",
"graphqurl": "^0.3.3",
"lodash.isequal": "^4.5.0",
"moment": "2.22.2",
"node-fetch": "2.2.0"
},
Expand Down
4 changes: 4 additions & 0 deletions run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
export TEST_HGE_URL=http://localhost:8000
export TEST_X_HASURA_ADMIN_SECRET=myadminsecretkey
npm run test
16 changes: 14 additions & 2 deletions src/import/generateTables.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
const throwError = require('./error');

const getDataType = (data, column) => {
if (typeof data === 'string' && data.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/)) {
return 'uuid';
}
if (typeof data === 'number' && Number.isInteger(data)) {
if (column === 'id') return 'serial';
return 'int';
}
if (typeof data === 'number') {
return 'numeric';
}
Expand Down Expand Up @@ -86,16 +93,21 @@ const sanitizeData = db => {
const generate = db => {
const metaData = [];
Object.keys(db).forEach(rootField => {
const hasPK = hasPrimaryKey(db[rootField], rootField);
const tableMetadata = {};
if (!hasPrimaryKey(db[rootField], rootField)) {
throwError(`message: a unique column with name "id" must present in table "${rootField}"`);
tableMetadata.primaryKeys = [];
if (hasPK) {
tableMetadata.primaryKeys = ['id'];
}
tableMetadata.name = rootField;
tableMetadata.columns = getColumnData(db[rootField], db);
tableMetadata.dependencies = [];
tableMetadata.columns.forEach(column => {
if (column.isForeign) {
tableMetadata.dependencies.push(column.name.substring(0, column.name.length - 3));
if (!hasPK) {
tableMetadata.primaryKeys.push(column.name);
}
}
});
metaData.push(tableMetadata);
Expand Down
8 changes: 7 additions & 1 deletion src/import/import.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@ const importData = async (jsonDb, url, headers, overwrite) => {
cli.action.stop('Done!');
cli.action.start('Inserting data');
const insertOrder = getInsertOrder(tables);
insertData(insertOrder, db, tables, url, headers);
insertData(insertOrder, db, tables, url, headers).then(() => {
// eslint-disable-next-line max-nested-callbacks
const tablesNeedingResetIdSequences = tables.filter(t => t.columns.some(c => c.name === 'id' && c.type === 'serial'));
// eslint-disable-next-line max-nested-callbacks
const resetSQL = tablesNeedingResetIdSequences.map(t => `SELECT setval(pg_get_serial_sequence('"public"."${t.name}"', 'id'), max(id)) FROM public."${t.name}";`);
runSql(resetSQL, url, headers);
});
});
});
});
Expand Down
2 changes: 1 addition & 1 deletion src/import/insert.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const transformData = (data, tables) => {
newRow[column.name] = moment(row[column.name]).format();
}
if (column.type === 'jsonb' && row[column.name]) {
newRow[column.name] = JSON.stringify(row[column.name]);
newRow[column.name] = JSON.parse(JSON.stringify(row[column.name]));
}
});
newData[table.name].push(newRow);
Expand Down
8 changes: 5 additions & 3 deletions src/import/relationships.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ const fetch = require('node-fetch');
const throwError = require('./error');

const getObjRelationshipName = dep => {
const relName = `${dep}By${dep[0].toUpperCase()}`;
return dep.length === 1 ? relName + 'Id' : relName + dep.substring(1, dep.length) + 'Id';
return dep;
};

const getArrayRelationshipName = (table, parent) => {
const relName = `${table}sBy${parent[0].toUpperCase()}`;
if (table.indexOf('__' > 0)) {
return table;
}
const relName = `${table}By${parent[0].toUpperCase()}`;
return parent.length === 1 ? `${relName}Id` : `${relName}${parent.substring(1, parent.length)}Id`;
};

Expand Down
10 changes: 8 additions & 2 deletions src/import/sql.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,19 @@ const generateCreateTableSql = metadata => {
let columnSql = '(';
table.columns.forEach((column, i) => {
if (column.name === 'id') {
columnSql += `"id" ${column.type} not null primary key`;
columnSql += `"id" ${column.type} not null`;
} else {
columnSql += `"${column.name}" ${column.type}`;
}
if (column.name === 'id' && column.type === 'uuid') {
columnSql += ' default gen_random_uuid()';
}
if (table.columns.length === i + 1) {
columnSql += `, primary key("${table.primaryKeys.join('", "')}")`;
}
columnSql += (table.columns.length === i + 1) ? ' ) ' : ', ';
});
const createTableSql = `create table public."${table.name}" ${columnSql};`;
let createTableSql = `create table public."${table.name}" ${columnSql};`;
sqlArray.push(createTableSql);
});
return sqlArray;
Expand Down
16 changes: 16 additions & 0 deletions test/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,22 @@ const db = {
j2g_test_favoriteRoutes: [
{id: 1, j2g_test_users_id: 1, j2g_test_routes_id: 1, datetime: '2018-07-01 15:48:45'},
],
j2g_test_uuids: [
{id: 1, version1: 'b87b8d7c-f9f0-11e9-8f0b-362b9e155667', version4: 'ca1325f8-612f-4672-841f-a8d84434e6a6'}
],
j2g_test_tags: [
{id: 1, name: 'tag a'},
{id: 2, name: 'tag b'}
],
j2g_test_user_tags: [
{j2g_test_users_id: 1, j2g_test_tags_id: 1},
{j2g_test_users_id: 1, j2g_test_tags_id: 2}
],
j2g_test_serialPrimaryKeys: [
{id: 1, name: 'a'},
{id: 3, name: 'b'},
{id: 6, name: 'c'}
]
};

module.exports = db;
41 changes: 41 additions & 0 deletions test/tests/complexQuery.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const query = `
query {
j2g_test_favoriteRoutes {
j2g_test_routesByJ2g_test_routesId {
j2g_test_leaguesByJ2g_test_leaguesId {
j2g_test_flightssByJ2g_test_leaguesId (
order_by: {
id:asc
}
){
j2g_test_flightCommentssByJ2g_test_flightsId(order_by: {j2g_test_users_id:asc}) {
j2g_test_users_id
j2g_test_usersByJ2g_test_usersId {
email
}
}
}
}
}
}
}
`;

const run = response => {
if (
response.data.j2g_test_favoriteRoutes[0].j2g_test_routesByJ2g_test_routesId
.j2g_test_leaguesByJ2g_test_leaguesId
.j2g_test_flightssByJ2g_test_leaguesId[0]
.j2g_test_flightCommentssByJ2g_test_flightsId[0]
.j2g_test_usersByJ2g_test_usersId.email === '[email protected]'
) {
return true;
}
return 'Unexpected response.';
};

module.exports = {
name: 'Complex query returns correct data',
query,
run,
};
24 changes: 24 additions & 0 deletions test/tests/compoundKeys.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const query = `
query {
j2g_test_user_tags(where: {j2g_test_users_id: {_eq: 1}}) {
j2g_test_users_id
j2g_test_tags_id
}
}
`;

const run = ({data}) => {
const returnedValue = data.j2g_test_user_tags;
if (returnedValue.length === 2) {
return true;
}
return `
Returned value:
${JSON.stringify(returnedValue, null, 2)}`;
};

module.exports = {
name: 'Compound key records found',
query,
run,
};
28 changes: 28 additions & 0 deletions test/tests/jsonb.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const isEqual = require('lodash.isequal');

const query = `
query {
j2g_test_users(where: {id: {_eq: 1}}) {
object
}
}
`;

const run = ({data}) => {
const returnedValue = data.j2g_test_users[0].object;
const expectedValue = {hey: 'there', whats: 'up'};
if (isEqual(expectedValue, returnedValue)) {
return true;
}
return `
Expected value:
${JSON.stringify(expectedValue, null, 2)}
Returned value:
${JSON.stringify(returnedValue, null, 2)}`;
};

module.exports = {
name: 'JSONB column returns JSON',
query,
run,
};
27 changes: 27 additions & 0 deletions test/tests/serialPrimaryKey.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const query = `
mutation InsertMutation {
insert_j2g_test_serialPrimaryKeys(objects: {name: "d"}) {
returning {
id
name
}
}
}
`;

const run = ({data}) => {
const returnedValue = data.insert_j2g_test_serialPrimaryKeys.returning[0].id;
const expectedValue = 7;
if (returnedValue === expectedValue) {
return true;
}
return `
Returned id did not equal ${expectedValue}:
${JSON.stringify(returnedValue, null, 2)}`;
};

module.exports = {
name: 'Integer primary key auto increments',
query,
run,
};
27 changes: 27 additions & 0 deletions test/tests/uuid.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const query = `
mutation InsertMutation {
insert_j2g_test_uuids(objects: {id: 2}) {
returning {
version4
version1
}
}
}
`;

const run = ({data}) => {
const returnedValue = data.insert_j2g_test_uuids.returning[0];
if (typeof returnedValue.version1 === 'string' && returnedValue.version1.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/) &&
typeof returnedValue.version4 === 'string' && returnedValue.version4.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/)) {
return true;
}
return `
Returned values are not UUIDS:
${JSON.stringify(returnedValue, null, 2)}`;
};

module.exports = {
name: 'UUID data saved as UUID column',
query,
run,
};
Loading