add all PG column types in table add / modify (close #223, #2035, #394, #2038) (#1933)

This commit is contained in:
Karthik Venkateswaran 2019-05-22 16:53:30 +05:30 committed by Rikin Kachhia
parent 32f1d47baa
commit 8a9901d417
47 changed files with 1561 additions and 562 deletions

View File

@ -8,8 +8,8 @@ export const dataTypes = [
'text',
'numeric',
'date',
'timestamp with time zone',
'time with time zone',
'timestamptz',
'timetz',
'boolean',
];
export const typeDefaults = {
@ -19,8 +19,8 @@ export const typeDefaults = {
text: 'test-text',
numeric: '0.55555',
date: 'now()',
'timestamp with time zone': 'now()',
'time with time zone': 'now()',
timestamptz: 'now()',
timetz: 'now()',
boolean: 'false',
};
export const queryTypes = ['insert', 'select', 'update', 'delete'];
@ -28,6 +28,14 @@ export const getColName = i => `apic_test_column_${i}`;
export const getTableName = (i, testName = '') =>
`apic_test_table_${testName}_${i}`;
export const getElementFromAlias = alias => `[data-test=${alias}]`;
export const getElementFromClassName = cn => `.${cn}`;
export const tableColumnTypeSelector = alias => {
cy.get(`${getElementFromAlias(alias)}`)
.children('div')
.click()
.find('input')
.focus();
};
export const makeDataAPIUrl = dataApiUrl => `${dataApiUrl}/v1/query`;
export const makeDataAPIOptions = (dataApiUrl, key, body) => ({
method: 'POST',

View File

@ -1,6 +1,10 @@
/* eslint import/prefer-default-export: 0 */
import { getElementFromAlias, baseUrl } from '../../../helpers/dataHelpers';
import {
getElementFromAlias,
baseUrl,
tableColumnTypeSelector,
} from '../../../helpers/dataHelpers';
import { validateCT } from '../../validators/validators';
import { makeDataAPIOptions } from '../../../helpers/dataHelpers';
import { toggleOnMigrationMode } from '../../data/migration-mode/utils';
@ -29,11 +33,21 @@ export const createTestTable = () => {
cy.get(getElementFromAlias('column-0'))
.clear()
.type('id');
cy.get(getElementFromAlias('col-type-0')).select('serial');
// cy.get(getElementFromAlias('col-type-0')).click();
tableColumnTypeSelector('col-type-0');
cy.get(getElementFromAlias('data_test_column_type_value_serial'))
.first()
.click();
// cy.get(getElementFromAlias('col-type-0')).select('serial');
cy.get(getElementFromAlias('column-1'))
.clear()
.type('name');
cy.get(getElementFromAlias('col-type-1')).select('text');
tableColumnTypeSelector('col-type-1');
cy.get(getElementFromAlias('data_test_column_type_value_text'))
.first()
.click();
// cy.get(getElementFromAlias('col-type-1')).select('text');
// Set primary key
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
// Click on create

View File

@ -1,4 +1,5 @@
import {
tableColumnTypeSelector,
getElementFromAlias,
getTableName,
getColName,
@ -34,7 +35,10 @@ export const failCTWithoutColumns = () => {
export const failCTWithoutPK = () => {
// Set first column
cy.get(getElementFromAlias('column-0')).type(getColName(0));
cy.get(getElementFromAlias('col-type-0')).select('serial');
tableColumnTypeSelector('col-type-0');
cy.get(getElementFromAlias('data_test_column_type_value_serial'))
.first()
.click();
// Click on create
cy.get(getElementFromAlias('table-create')).click();
// Check for an error
@ -48,7 +52,10 @@ export const failCTWithoutPK = () => {
export const failCTDuplicateColumns = () => {
// Set second column
cy.get(getElementFromAlias('column-1')).type(getColName(0));
cy.get(getElementFromAlias('col-type-1')).select('serial');
tableColumnTypeSelector('col-type-1');
cy.get(getElementFromAlias('data_test_column_type_value_serial'))
.first()
.click();
// Set primary key
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
// Click on create
@ -65,12 +72,41 @@ export const failCTDuplicateColumns = () => {
validateCT(getTableName(0, testName), 'failure');
};
export const failCTDuplicatePrimaryKey = () => {
// Set second column
cy.get(getElementFromAlias('column-1'))
.clear()
.type(getColName(1));
tableColumnTypeSelector('col-type-1');
cy.get(getElementFromAlias('data_test_column_type_value_serial'))
.first()
.click();
// Set primary key
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
cy.get(getElementFromAlias('primary-key-select-1')).select('0');
cy.on('window:alert', str => {
expect(
str ===
`You key [${getColName(
0
)}] is already present in the current set of primary keys.`
).to.be.true;
});
// Check if the route didn't change
cy.url().should('eq', `${baseUrl}/data/schema/public/table/add`);
// Validate
validateCT(getTableName(0, testName), 'failure');
};
export const failCTWrongDefaultValue = () => {
// Set second column
cy.get(getElementFromAlias('column-1'))
.clear()
.type(getColName(1));
cy.get(getElementFromAlias('col-type-1')).select('integer');
tableColumnTypeSelector('col-type-1');
cy.get(getElementFromAlias('data_test_column_type_value_integer'))
.first()
.click();
cy.get(getElementFromAlias('col-default-1')).type('qwerty');
// Set primary key
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
@ -87,7 +123,10 @@ export const passCT = () => {
cy.get(getElementFromAlias('column-1'))
.clear()
.type(getColName(1));
cy.get(getElementFromAlias('col-type-1')).select('text');
tableColumnTypeSelector('col-type-1');
cy.get(getElementFromAlias('data_test_column_type_value_text'))
.first()
.click();
cy.get(getElementFromAlias('col-default-1')).clear();
// Set primary key
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
@ -114,13 +153,22 @@ export const passCTWithFK = () => {
.type(getTableName(1, testName));
// Set first column
cy.get(getElementFromAlias('column-0')).type(getColName(0));
cy.get(getElementFromAlias('col-type-0')).select('serial');
tableColumnTypeSelector('col-type-0');
cy.get(getElementFromAlias('data_test_column_type_value_serial'))
.first()
.click();
// Set second column
cy.get(getElementFromAlias('column-1')).type(getColName(1));
cy.get(getElementFromAlias('col-type-1')).select('text');
tableColumnTypeSelector('col-type-1');
cy.get(getElementFromAlias('data_test_column_type_value_text'))
.first()
.click();
// Set third column
cy.get(getElementFromAlias('column-2')).type(getColName(2));
cy.get(getElementFromAlias('col-type-2')).select('text');
tableColumnTypeSelector('col-type-2');
cy.get(getElementFromAlias('data_test_column_type_value_text'))
.first()
.click();
// Set primary key
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
@ -170,7 +218,10 @@ export const failCTDuplicateTable = () => {
cy.get(getElementFromAlias('tableName')).type(getTableName(0, testName));
// Set column
cy.get(getElementFromAlias('column-0')).type(getColName(1));
cy.get(getElementFromAlias('col-type-0')).select('serial');
tableColumnTypeSelector('col-type-0');
cy.get(getElementFromAlias('data_test_column_type_value_serial'))
.first()
.click();
// Set primary key
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
// Click on create

View File

@ -5,6 +5,7 @@ import {
dataTypes,
getElementFromAlias,
typeDefaults,
tableColumnTypeSelector,
} from '../../../helpers/dataHelpers';
import {
@ -24,7 +25,12 @@ const setColumns = () => {
// Type column name
cy.get(getElementFromAlias(`column-${i}`)).type(getColName(i));
// Select column type
cy.get(getElementFromAlias(`col-type-${i}`)).select(dataTypes[i]);
tableColumnTypeSelector(`col-type-${i}`);
// cy.get(getElementFromAlias(`col-type-${i}`)).click();
cy.get(getElementFromAlias(`data_test_column_type_value_${dataTypes[i]}`))
.first()
.click();
// cy.get(getElementFromAlias(`col-type-${i}`)).select(dataTypes[i]);
if (i === dataTypes.indexOf('text')) {
cy.get(getElementFromAlias(`unique-${i}`)).check();
@ -109,7 +115,11 @@ export const passSearchTables = () => {
// Type column name
cy.get(getElementFromAlias('column-0')).type(getColName(0));
// Select column type
cy.get(getElementFromAlias('col-type-0')).select('integer');
// cy.get(getElementFromAlias('col-type-0')).select('integer');
tableColumnTypeSelector('col-type-0');
cy.get(getElementFromAlias('data_test_column_type_value_integer'))
.first()
.click();
// Set primary key
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
// Click on create
@ -425,9 +435,18 @@ export const checkViewRelationship = () => {
// Type table name
cy.get(getElementFromAlias('tableName')).type(getTableName(2, testName));
cy.get(getElementFromAlias('column-0')).type('id');
cy.get(getElementFromAlias('col-type-0')).select('serial');
tableColumnTypeSelector('col-type-0');
// cy.get(getElementFromAlias('col-type-0')).click();
cy.get(getElementFromAlias('data_test_column_type_value_serial'))
.first()
.click();
cy.get(getElementFromAlias('column-1')).type('someID');
cy.get(getElementFromAlias('col-type-1')).select('integer');
tableColumnTypeSelector('col-type-1');
// cy.get(getElementFromAlias('col-type-1')).click();
cy.get(getElementFromAlias('data_test_column_type_value_integer'))
.first()
.click();
// cy.get(getElementFromAlias('col-type-1')).select('integer');
// Set primary key
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
// Click on create

View File

@ -1,4 +1,5 @@
import {
tableColumnTypeSelector,
baseUrl,
getTableName,
getColName,
@ -18,7 +19,11 @@ export const passMTCreateTable = () => {
cy.url().should('eq', `${baseUrl}/data/schema/public/table/add`);
cy.get(getElementFromAlias('tableName')).type(getTableName(0, testName));
cy.get(getElementFromAlias('column-0')).type('id');
cy.get(getElementFromAlias('col-type-0')).select('Integer');
tableColumnTypeSelector('col-type-0');
cy.get(getElementFromAlias('data_test_column_type_value_integer'))
.first()
.click();
// cy.get(getElementFromAlias('col-type-0')).select('Integer');
cy.get(getElementFromAlias('primary-key-select-0')).select('id');
cy.get(getElementFromAlias('table-create')).click();
cy.wait(7000);
@ -106,7 +111,11 @@ export const failMTWithoutColType = () => {
export const Addcolumnnullable = () => {
cy.get(getElementFromAlias('column-name')).type('{selectall}{del}');
cy.get(getElementFromAlias('column-name')).type(getColName(3));
cy.get(getElementFromAlias('data-type')).select('Text');
tableColumnTypeSelector('col-type-0');
cy.get(getElementFromAlias('data_test_column_type_value_text'))
.first()
.click();
// cy.get(getElementFromAlias('data-type')).select('Text');
cy.get(getElementFromAlias('nullable-checkbox')).uncheck({ force: true });
cy.get(getElementFromAlias('add-column-button')).click();
cy.wait(2500);
@ -121,7 +130,12 @@ export const Addcolumnnullable = () => {
export const Addcolumnname = name => {
cy.get(getElementFromAlias('column-name')).type('{selectall}{del}');
cy.get(getElementFromAlias('column-name')).type(name);
cy.get(getElementFromAlias('data-type')).select('integer');
tableColumnTypeSelector('col-type-0');
cy.get(getElementFromAlias('data_test_column_type_value_integer'))
.first()
.click();
// cy.get(getElementFromAlias('data-type')).select('integer');
cy.get(getElementFromAlias('add-column-button')).click();
cy.wait(5000);
@ -131,7 +145,11 @@ export const Addcolumnname = name => {
export const passMTAddColumn = () => {
cy.get(getElementFromAlias('column-name')).type('{selectall}{del}');
cy.get(getElementFromAlias('column-name')).type(getColName(0));
cy.get(getElementFromAlias('data-type')).select('integer');
tableColumnTypeSelector('col-type-0');
cy.get(getElementFromAlias('data_test_column_type_value_integer'))
.first()
.click();
// cy.get(getElementFromAlias('data-type')).select('integer');
cy.get(getElementFromAlias('add-column-button')).click();
cy.wait(5000);
// cy.get('.notification-success').click();
@ -287,14 +305,14 @@ export const Createtable = (name, dict) => {
export const Createtables = () => {
cy.get(getElementFromAlias('data-create-table')).click();
Createtable('author', { id: 'Integer', name: 'Text' });
Createtable('author', { id: 'integer', name: 'Text' });
cy.get(getElementFromAlias('sidebar-add-table')).click();
Createtable('article', {
id: 'Integer',
title: 'Text',
Content: 'Text',
author_id: 'Integer',
rating: 'Integer',
id: 'integer',
title: 'text',
Content: 'text',
author_id: 'integer',
rating: 'integer',
});
};

View File

@ -1,4 +1,5 @@
import {
tableColumnTypeSelector,
baseUrl,
getTableName,
getElementFromAlias,
@ -21,13 +22,26 @@ export const passPTCreateTable = () => {
cy.get(getElementFromAlias('tableName')).type(getTableName(0, testName));
// Set first column
cy.get(getElementFromAlias('column-0')).type(getColName(0));
cy.get(getElementFromAlias('col-type-0')).select('serial');
tableColumnTypeSelector('col-type-0');
cy.get(getElementFromAlias('data_test_column_type_value_serial'))
.first()
.click();
// cy.get(getElementFromAlias('col-type-0')).select('serial');
// Set second column
cy.get(getElementFromAlias('column-1')).type(getColName(1));
cy.get(getElementFromAlias('col-type-1')).select('integer');
tableColumnTypeSelector('col-type-1');
cy.get(getElementFromAlias('data_test_column_type_value_integer'))
.first()
.click();
// cy.get(getElementFromAlias('col-type-1')).select('integer');
// Set third column
cy.get(getElementFromAlias('column-2')).type(getColName(2));
cy.get(getElementFromAlias('col-type-2')).select('text');
tableColumnTypeSelector('col-type-2');
cy.get(getElementFromAlias('data_test_column_type_value_text'))
.first()
.click();
// cy.get(getElementFromAlias('col-type-2')).select('text');
// Set primary key
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
// Create

View File

@ -1,4 +1,8 @@
import { baseUrl, getElementFromAlias } from '../../../helpers/dataHelpers';
import {
baseUrl,
getElementFromAlias,
tableColumnTypeSelector,
} from '../../../helpers/dataHelpers';
import {
setMetaData,
@ -28,7 +32,11 @@ export const Createtable = (name, fields) => {
for (const key in fields) {
if (fields.hasOwnProperty(key)) {
cy.get(getElementFromAlias(`column-${i}`)).type(key);
cy.get(getElementFromAlias(`col-type-${i}`)).select(fields[key]);
tableColumnTypeSelector(`col-type-${i}`);
cy.get(getElementFromAlias(`data_test_column_type_value_${fields[key]}`))
.first()
.click();
// cy.get(getElementFromAlias(`col-type-${i}`)).select(fields[key]);
i++;
}
}
@ -75,19 +83,19 @@ export const Createtable = (name, fields) => {
};
export const passRTCreateTables = () => {
Createtable('author', { id: 'Integer', name: 'Text' });
Createtable('author', { id: 'integer', name: 'text' });
Createtable('article', {
id: 'Integer',
title: 'Text',
Content: 'Text',
author_id: 'Integer',
rating: 'Integer',
id: 'integer',
title: 'text',
Content: 'text',
author_id: 'integer',
rating: 'integer',
});
Createtable('comment', {
id: 'Integer',
user_id: 'Integer',
article_id: 'Integer',
comment: 'Text',
id: 'integer',
user_id: 'integer',
article_id: 'integer',
comment: 'text',
});
};

View File

@ -1,4 +1,8 @@
import { getElementFromAlias, baseUrl } from '../../../helpers/dataHelpers';
import {
getElementFromAlias,
baseUrl,
tableColumnTypeSelector,
} from '../../../helpers/dataHelpers';
import {
setMetaData,
@ -17,7 +21,10 @@ export const Createtable = (name, dict) => {
const values = Object.keys(dict).map(k => dict[k]);
for (let i = 0; i < keys.length; i += 1) {
cy.get(getElementFromAlias(`column-${i}`)).type(keys[i]);
cy.get(getElementFromAlias(`col-type-${i}`)).select(values[i]);
tableColumnTypeSelector(`col-type-${i}`);
cy.get(getElementFromAlias(`data_test_column_type_value_${values[i]}`))
.first()
.click();
}
cy.get(getElementFromAlias('primary-key-select-0')).select('id');
cy.get(getElementFromAlias('table-create')).click();
@ -32,21 +39,21 @@ export const Createtable = (name, dict) => {
export const passVCreateTables = () => {
cy.get(getElementFromAlias('data-create-table')).click();
Createtable('author', { id: 'Integer', name: 'Text' });
Createtable('author', { id: 'integer', name: 'text' });
cy.get(getElementFromAlias('sidebar-add-table')).click();
Createtable('article', {
id: 'Integer',
title: 'Text',
Content: 'Text',
author_id: 'Integer',
rating: 'Integer',
id: 'integer',
title: 'text',
Content: 'text',
author_id: 'integer',
rating: 'integer',
});
cy.get(getElementFromAlias('sidebar-add-table')).click();
Createtable('comment', {
id: 'Integer',
user_id: 'Integer',
article_id: 'Integer',
comment: 'Text',
id: 'integer',
user_id: 'integer',
article_id: 'integer',
comment: 'text',
});
};

View File

@ -8,7 +8,10 @@ import {
getTimeoutSeconds,
baseUrl,
} from '../../../helpers/eventHelpers';
import { getColName } from '../../../helpers/dataHelpers';
import {
getColName,
tableColumnTypeSelector,
} from '../../../helpers/dataHelpers';
import {
setMetaData,
validateCT,
@ -31,13 +34,26 @@ export const passPTCreateTable = () => {
cy.get(getElementFromAlias('tableName')).type(getTableName(0, testName));
// Set first column
cy.get(getElementFromAlias('column-0')).type(getColName(0));
cy.get(getElementFromAlias('col-type-0')).select('serial');
tableColumnTypeSelector('col-type-0');
cy.get(getElementFromAlias('data_test_column_type_value_serial'))
.first()
.click();
// cy.get(getElementFromAlias('col-type-0')).select('serial');
// Set second column
cy.get(getElementFromAlias('column-1')).type(getColName(1));
cy.get(getElementFromAlias('col-type-1')).select('integer');
tableColumnTypeSelector('col-type-1');
cy.get(getElementFromAlias('data_test_column_type_value_integer'))
.first()
.click();
// cy.get(getElementFromAlias('col-type-1')).select('integer');
// Set third column
cy.get(getElementFromAlias('column-2')).type(getColName(2));
cy.get(getElementFromAlias('col-type-2')).select('text');
tableColumnTypeSelector('col-type-2');
cy.get(getElementFromAlias('data_test_column_type_value_text'))
.first()
.click();
// cy.get(getElementFromAlias('col-type-2')).select('text');
// Set primary key
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
// Create

View File

@ -370,7 +370,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz",
"integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==",
"dev": true,
"requires": {
"@babel/types": "^7.0.0"
}
@ -1559,7 +1558,6 @@
"version": "7.3.3",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.3.tgz",
"integrity": "sha512-2tACZ80Wg09UnPg5uGAOUvvInaqLk3l/IAhQzlxLQOIXacr6bMsra5SH6AWw/hIDRCSbCdHP2KzSOD+cT7TzMQ==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
"lodash": "^4.17.11",
@ -1569,8 +1567,7 @@
"to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
"dev": true
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
}
}
},
@ -1613,6 +1610,62 @@
}
}
},
"@emotion/babel-utils": {
"version": "0.6.10",
"resolved": "https://registry.npmjs.org/@emotion/babel-utils/-/babel-utils-0.6.10.tgz",
"integrity": "sha512-/fnkM/LTEp3jKe++T0KyTszVGWNKPNOUJfjNKLO17BzQ6QPxgbg3whayom1Qr2oLFH3V92tDymU+dT5q676uow==",
"requires": {
"@emotion/hash": "^0.6.6",
"@emotion/memoize": "^0.6.6",
"@emotion/serialize": "^0.9.1",
"convert-source-map": "^1.5.1",
"find-root": "^1.1.0",
"source-map": "^0.7.2"
},
"dependencies": {
"source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
}
}
},
"@emotion/hash": {
"version": "0.6.6",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.6.6.tgz",
"integrity": "sha512-ojhgxzUHZ7am3D2jHkMzPpsBAiB005GF5YU4ea+8DNPybMk01JJUM9V9YRlF/GE95tcOm8DxQvWA2jq19bGalQ=="
},
"@emotion/memoize": {
"version": "0.6.6",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.6.6.tgz",
"integrity": "sha512-h4t4jFjtm1YV7UirAFuSuFGyLa+NNxjdkq6DpFLANNQY5rHueFZHVY+8Cu1HYVP6DrheB0kv4m5xPjo7eKT7yQ=="
},
"@emotion/serialize": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.9.1.tgz",
"integrity": "sha512-zTuAFtyPvCctHBEL8KZ5lJuwBanGSutFEncqLn/m9T1a6a93smBStK+bZzcNPgj4QS8Rkw9VTwJGhRIUVO8zsQ==",
"requires": {
"@emotion/hash": "^0.6.6",
"@emotion/memoize": "^0.6.6",
"@emotion/unitless": "^0.6.7",
"@emotion/utils": "^0.8.2"
}
},
"@emotion/stylis": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.7.1.tgz",
"integrity": "sha512-/SLmSIkN13M//53TtNxgxo57mcJk/UJIDFRKwOiLIBEyBHEcipgR6hNMQ/59Sl4VjCJ0Z/3zeAZyvnSLPG/1HQ=="
},
"@emotion/unitless": {
"version": "0.6.7",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.6.7.tgz",
"integrity": "sha512-Arj1hncvEVqQ2p7Ega08uHLr1JuRYBuO5cIvcA+WWEQ5+VmkOE3ZXzl04NbQxeQpWX78G7u6MqxKuNX3wvYZxg=="
},
"@emotion/utils": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.8.2.tgz",
"integrity": "sha512-rLu3wcBWH4P5q1CGoSSH/i9hrXs7SlbRLkoq9IGuoPYNGQvDJ3pt/wmOM+XgYjIDRMVIdkUWt0RsfzF50JfnCw=="
},
"@webassemblyjs/ast": {
"version": "1.7.8",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.8.tgz",
@ -1800,8 +1853,7 @@
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
"accepts": {
"version": "1.3.5",
@ -2409,6 +2461,32 @@
}
}
},
"babel-plugin-emotion": {
"version": "9.2.11",
"resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-9.2.11.tgz",
"integrity": "sha512-dgCImifnOPPSeXod2znAmgc64NhaaOjGEHROR/M+lmStb3841yK1sgaDYAYMnlvWNz8GnpwIPN0VmNpbWYZ+VQ==",
"requires": {
"@babel/helper-module-imports": "^7.0.0",
"@emotion/babel-utils": "^0.6.4",
"@emotion/hash": "^0.6.2",
"@emotion/memoize": "^0.6.1",
"@emotion/stylis": "^0.7.0",
"babel-plugin-macros": "^2.0.0",
"babel-plugin-syntax-jsx": "^6.18.0",
"convert-source-map": "^1.5.0",
"find-root": "^1.1.0",
"mkdirp": "^0.5.1",
"source-map": "^0.5.7",
"touch": "^2.0.1"
},
"dependencies": {
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
}
}
},
"babel-plugin-istanbul": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.1.tgz",
@ -2465,6 +2543,73 @@
}
}
},
"babel-plugin-macros": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.5.1.tgz",
"integrity": "sha512-xN3KhAxPzsJ6OQTktCanNpIFnnMsCV+t8OloKxIL72D6+SUZYFn9qfklPgef5HyyDtzYZqqb+fs1S12+gQY82Q==",
"requires": {
"@babel/runtime": "^7.4.2",
"cosmiconfig": "^5.2.0",
"resolve": "^1.10.0"
},
"dependencies": {
"@babel/runtime": {
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.4.tgz",
"integrity": "sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg==",
"requires": {
"regenerator-runtime": "^0.13.2"
}
},
"cosmiconfig": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
"integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
"requires": {
"import-fresh": "^2.0.0",
"is-directory": "^0.3.1",
"js-yaml": "^3.13.1",
"parse-json": "^4.0.0"
}
},
"js-yaml": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"requires": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
}
},
"regenerator-runtime": {
"version": "0.13.2",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz",
"integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA=="
},
"resolve": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz",
"integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==",
"requires": {
"path-parse": "^1.0.6"
}
}
}
},
"babel-plugin-syntax-jsx": {
"version": "6.18.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
"integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY="
},
"babel-plugin-transform-react-remove-prop-types": {
"version": "0.4.18",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.18.tgz",
@ -2949,6 +3094,21 @@
"os-homedir": "^1.0.1"
}
},
"caller-callsite": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
"integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
"requires": {
"callsites": "^2.0.0"
},
"dependencies": {
"callsites": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
"integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA="
}
}
},
"caller-path": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
@ -3604,7 +3764,6 @@
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz",
"integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==",
"dev": true,
"requires": {
"safe-buffer": "~5.1.1"
}
@ -3698,6 +3857,20 @@
"elliptic": "^6.0.0"
}
},
"create-emotion": {
"version": "9.2.12",
"resolved": "https://registry.npmjs.org/create-emotion/-/create-emotion-9.2.12.tgz",
"integrity": "sha512-P57uOF9NL2y98Xrbl2OuiDQUZ30GVmASsv5fbsjF4Hlraip2kyAvMm+2PoYUvFFw03Fhgtxk3RqZSm2/qHL9hA==",
"requires": {
"@emotion/hash": "^0.6.2",
"@emotion/memoize": "^0.6.1",
"@emotion/stylis": "^0.7.0",
"@emotion/unitless": "^0.6.2",
"csstype": "^2.5.2",
"stylis": "^3.5.0",
"stylis-rule-sheet": "^0.0.10"
}
},
"create-hash": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
@ -3926,6 +4099,11 @@
}
}
},
"csstype": {
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.4.tgz",
"integrity": "sha512-lAJUJP3M6HxFXbqtGRc0iZrdyeN+WzOWeY0q/VnFzI+kqVrYIzC7bWlKqCW7oCIdzoPkvfp82EVvrTlQ8zsWQg=="
},
"currently-unhandled": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
@ -4405,6 +4583,15 @@
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
"dev": true
},
"emotion": {
"version": "9.2.12",
"resolved": "https://registry.npmjs.org/emotion/-/emotion-9.2.12.tgz",
"integrity": "sha512-hcx7jppaI8VoXxIWEhxpDW7I+B4kq9RNzQLmsrF6LY8BGKqe2N+gFAQr0EfuFucFlPs2A9HM4+xNj4NeqEWIOQ==",
"requires": {
"babel-plugin-emotion": "^9.2.11",
"create-emotion": "^9.2.12"
}
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
@ -4456,7 +4643,6 @@
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"dev": true,
"requires": {
"is-arrayish": "^0.2.1"
}
@ -4920,8 +5106,7 @@
"esutils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
"dev": true
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
},
"etag": {
"version": "1.8.1",
@ -5403,6 +5588,11 @@
"integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=",
"dev": true
},
"find-root": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
},
"find-up": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
@ -7199,6 +7389,30 @@
"integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=",
"optional": true
},
"import-fresh": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
"integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
"requires": {
"caller-path": "^2.0.0",
"resolve-from": "^3.0.0"
},
"dependencies": {
"caller-path": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
"integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
"requires": {
"caller-callsite": "^2.0.0"
}
},
"resolve-from": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
}
}
},
"import-local": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
@ -7492,8 +7706,7 @@
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
"dev": true
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
},
"is-binary-path": {
"version": "1.0.1",
@ -7566,8 +7779,7 @@
"is-directory": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
"integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
"dev": true
"integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE="
},
"is-dotfile": {
"version": "1.0.3",
@ -7952,8 +8164,7 @@
"json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
"dev": true
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
},
"json-schema": {
"version": "0.2.3",
@ -8925,6 +9136,11 @@
"p-is-promise": "^1.1.0"
}
},
"memoize-one": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.0.4.tgz",
"integrity": "sha512-P0z5IeAH6qHHGkJIXWw0xC2HNEgkx/9uWWBQw64FJj3/ol14VYdfVGWWr0fXfjhhv3TKVIqUq65os6O4GUNksA=="
},
"memory-fs": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
@ -11010,8 +11226,7 @@
"path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"dev": true
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
},
"path-to-regexp": {
"version": "0.1.7",
@ -12016,6 +12231,14 @@
"integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
"dev": true
},
"raf": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
"requires": {
"performance-now": "^2.1.0"
}
},
"ramda": {
"version": "0.24.1",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz",
@ -12257,6 +12480,14 @@
}
}
},
"react-input-autosize": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-2.2.1.tgz",
"integrity": "sha512-3+K4CD13iE4lQQ2WlF8PuV5htfmTRLH6MDnfndHM6LuBRszuXnuyIfE7nhSKt8AzRBZ50bu0sAhkNMeS5pxQQA==",
"requires": {
"prop-types": "^15.5.8"
}
},
"react-is": {
"version": "16.5.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.5.2.tgz",
@ -12390,6 +12621,20 @@
"resolved": "https://registry.npmjs.org/react-router-redux/-/react-router-redux-4.0.8.tgz",
"integrity": "sha1-InQDWWtRUeGCN32rg1tdRfD4BU4="
},
"react-select": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/react-select/-/react-select-2.4.3.tgz",
"integrity": "sha512-cmxNaiHpviRYkojeW9rGEUJ4jpX7QTmPe2wcscwA4d1lStzw/cJtr4ft5H2O/YhfpkrcwaLghu3XmEYdXhBo8Q==",
"requires": {
"classnames": "^2.2.5",
"emotion": "^9.1.2",
"memoize-one": "^5.0.0",
"prop-types": "^15.6.0",
"raf": "^3.4.0",
"react-input-autosize": "^2.2.1",
"react-transition-group": "^2.2.1"
}
},
"react-side-effect": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-1.1.5.tgz",
@ -14243,6 +14488,16 @@
}
}
},
"stylis": {
"version": "3.5.4",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
"integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q=="
},
"stylis-rule-sheet": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz",
"integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw=="
},
"subscriptions-transport-ws": {
"version": "0.9.15",
"resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.15.tgz",
@ -14656,6 +14911,24 @@
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
"integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI="
},
"touch": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/touch/-/touch-2.0.2.tgz",
"integrity": "sha512-qjNtvsFXTRq7IuMLweVgFxmEuQ6gLbRs2jQxL80TtZ31dEKWYIxRXquij6w6VimyDek5hD3PytljHmEtAs2u0A==",
"requires": {
"nopt": "~1.0.10"
},
"dependencies": {
"nopt": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
"integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
"requires": {
"abbrev": "1"
}
}
}
},
"tough-cookie": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",

View File

@ -89,6 +89,7 @@
"react-redux": "^5.0.6",
"react-router": "^3.2.0",
"react-router-redux": "^4.0.8",
"react-select": "^2.4.3",
"react-table": "^6.8.6",
"react-tabs": "^2.1.0",
"react-toggle": "^4.0.2",

View File

@ -280,6 +280,11 @@ button {
outline: none;
}
button > i {
padding-top: 3px;
padding-bottom: 3px;
}
.no_border {
border: 0 !important;
border-radius: 0 !important;

View File

@ -0,0 +1,70 @@
import React from 'react';
import Select, { components } from 'react-select';
import PropTypes from 'prop-types';
/*
* Wrap the option generated by react-select and adds utility properties
* */
const CustomOption = props => {
return (
<div
title={props.data.description || ''}
data-test={`data_test_column_type_value_${props.data.value}`}
>
<components.Option {...props} />
</div>
);
};
/*
* Searchable select box component
* 1) options: Accepts options
* 2) value: selectedValue
* 3) onChange: function to call on change of value
* 4) bsClass: Wrapper class
* 5) customStyle: Custom style
* */
const SearchableSelectBox = ({
options,
onChange,
value,
bsClass,
styleOverrides,
}) => {
/* Select element style customization */
const customStyles = {};
if (styleOverrides) {
Object.keys(styleOverrides).forEach(comp => {
customStyles[comp] = provided => {
return {
...provided,
...styleOverrides[comp],
};
};
});
}
return (
<Select
isSearchable
components={{ Option: CustomOption }}
classNamePrefix={`${bsClass}`}
placeholder="column_type"
options={options}
onChange={onChange}
value={value}
styles={customStyles}
/>
);
};
SearchableSelectBox.propTypes = {
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
options: PropTypes.array.isRequired,
bsClass: PropTypes.string,
customStyle: PropTypes.object,
};
export default SearchableSelectBox;

View File

@ -54,7 +54,7 @@
.ReactTable .rt-table .rt-thead .rt-td {
padding-left: 20px !important;
padding-right: 20px !important;
border-right: 1px solid rgba(0,0,0,0.1);
border-right: 1px solid rgba(0, 0, 0, 0.1);
}
.ReactTable .rt-table .rt-thead .rt-th.collapsed,
@ -66,11 +66,11 @@
.ReactTable .rt-tbody .rt-th,
.ReactTable .rt-tbody .rt-td {
padding: 8px;
border-right: 1px solid rgba(0,0,0,0.1);
border-right: 1px solid rgba(0, 0, 0, 0.1);
word-wrap: break-word;
}
.ReactTable .rt-th, .ReactTable .rt-td
{
.ReactTable .rt-th,
.ReactTable .rt-td {
overflow: unset !important;
}
.ReactTable .rt-th .ellipsis,
@ -105,8 +105,7 @@
.ReactTable .rt-table .rt-tbody .rt-tr-group .rt-tr.-odd:hover {
background-color: #ebf7de;
}
.ReactTable .rt-table
{
.ReactTable .rt-table {
overflow: unset;
}
.ReactTable .rt-table .rt-tbody {

View File

@ -150,10 +150,6 @@ a.expanded {
height: 34px;
}
.select200 {
width: 200px;
}
.defaultWidth {
width: 200px;
margin-right: 0px;
@ -211,10 +207,3 @@ a.expanded {
.tableCellExpanded {
white-space: normal;
}
.tableActionBtn {
i {
padding-top: 3px;
padding-bottom: 3px;
}
}

View File

@ -0,0 +1,21 @@
import React from 'react';
import PropTypes from 'prop-types';
const TextInput = props => {
const { type = 'text', placeholder = '', bsclass = null, onChange } = props;
return (
<input
{...props}
type={type}
placeholder={placeholder}
className={`${bsclass} form-control`}
onChange={onChange}
/>
);
};
TextInput.propTypes = {
onChange: PropTypes.func.isRequired,
};
export default TextInput;

View File

@ -323,9 +323,7 @@ class Main extends React.Component {
<div className={styles.socialIcon}>
<img
className="img img-responsive"
src={`${
globals.assetsPath
}/common/img/githubicon.png`}
src={`${globals.assetsPath}/common/img/githubicon.png`}
alt={'GitHub'}
/>
</div>
@ -356,9 +354,7 @@ class Main extends React.Component {
<div className={styles.socialIcon}>
<img
className="img img-responsive"
src={`${
globals.assetsPath
}/common/img/twittericon.png`}
src={`${globals.assetsPath}/common/img/twittericon.png`}
alt={'Twitter'}
/>
</div>

View File

@ -100,12 +100,6 @@ class Common extends React.Component {
<OverlayTrigger placement="right" overlay={graphqlurl}>
<i className="fa fa-question-circle" aria-hidden="true" />
</OverlayTrigger>
<br />
<br />
<small>
Note: Specifying the server URL via an environmental variable is
recommended if you have different URLs for multiple environments.
</small>
</h4>
<div className={styles.wd_300}>
<DropdownButton
@ -137,6 +131,11 @@ class Common extends React.Component {
testId="remote-schema-graphql-url"
/>
</div>
<br />
<small>
Note: Specifying the server URL via an environmental variable is
recommended if you have different URLs for multiple environments.
</small>
<div className={styles.subheading_text + ' ' + styles.addPaddTop}>
Headers for the remote GraphQL server
</div>

View File

@ -1,4 +1,5 @@
import defaultState from './AddState';
import _push from '../push';
import { loadSchema, makeMigrationCall } from '../DataActions';
import {
@ -30,6 +31,10 @@ const REQUEST_ERROR = 'AddTable/REQUEST_ERROR';
const VALIDATION_ERROR = 'AddTable/VALIDATION_ERROR';
const RESET_VALIDATION_ERROR = 'AddTable/RESET_VALIDATION_ERROR';
/*
* For any action dispatched, the ability to notify the renderer that something is happening
* */
const setDefaults = () => ({ type: SET_DEFAULTS });
const setTableName = value => ({ type: SET_TABLENAME, value });
const setTableComment = value => ({ type: SET_TABLECOMMENT, value });
@ -46,11 +51,10 @@ const setColDefault = (colDefault, index, isNull) => ({
index,
isNull,
});
const setColType = (coltype, index, isNull) => ({
const setColType = (coltype, index) => ({
type: SET_COLTYPE,
coltype,
index,
isNull,
});
const removeColDefault = index => ({ type: REMOVE_COLDEFAULT, index });
const setColNullable = (isNull, index) => ({
@ -383,7 +387,6 @@ const addTableReducer = (state = defaultState, action) => {
{
...state.columns[ij],
type: action.coltype,
nullable: action.isNull,
},
...state.columns.slice(ij + 1),
],

View File

@ -2,17 +2,20 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Helmet from 'react-helmet';
import * as tooltip from './Tooltips';
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
import Button from '../../../Common/Button/Button';
import PrimaryKeySelector from '../Common/ReusableComponents/PrimaryKeySelector';
import ForeignKeyWrapper from './ForeignKeyWrapper';
import UniqueKeyWrapper from './UniqueKeyWrapper';
import dataTypes from '../Common/DataTypes';
import { TIMESTAMP, DATE, UUID } from '../utils';
import { showErrorNotification } from '../../Common/Notification';
import TableName from './TableName';
import TableColumns from './TableColumns';
import TableComment from './TableComment';
import * as tooltip from './Tooltips';
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
import {
setTableName,
setTableComment,
@ -21,17 +24,15 @@ import {
setColType,
setColNullable,
setColDefault,
removeColDefault,
setForeignKeys,
validationError,
resetValidation,
setDefaults,
setPk,
createTableSql,
addCol,
setUniqueKeys,
} from './AddActions';
import { fetchColumnTypes, RESET_COLUMN_TYPE_LIST } from '../DataActions';
import { setDefaults, setPk, createTableSql } from './AddActions';
import { validationError, resetValidation } from './AddActions';
import {
ATLEAST_ONE_PRIMARY_KEY_MSG,
ATLEAST_ONE_COLUMN_MSG,
@ -48,35 +49,85 @@ import gqlPattern, {
gqlColumnErrorNotif,
} from '../Common/GraphQLValidation';
import styles from '../../../Common/TableCommon/Table.scss';
/*
const typeDescriptionDict = convertListToDictUsingKV(
'value',
'description',
dataTypes
);
*/
/* AddTable is a wrapper which wraps
* 1) Table Name input
* 2) Columns inputs
* 3) Primary Key input
* 4) Comment Input
* 5) Add Table button
* */
class AddTable extends Component {
constructor(props) {
super(props);
this.props.dispatch(setDefaults());
const { columns, dispatch } = this.props;
columns.map((column, i) => {
let defValue = '';
if ('default' in column) {
defValue = column.default.value;
}
if (defValue === '') {
dispatch(removeColDefault(i));
}
});
this.onTableNameChange = this.onTableNameChange.bind(this);
this.onTableCommentChange = this.onTableCommentChange.bind(this);
this.onRemoveColumn = this.onRemoveColumn.bind(this);
this.onColumnChange = this.onColumnNameChange.bind(this);
this.onColTypeChange = this.onColTypeChange.bind(this);
this.onColNullableChange = this.onColNullableChange.bind(this);
this.onColUniqueChange = this.onColUniqueChange.bind(this);
this.setColDefaultValue = this.setColDefaultValue.bind(this);
}
componentDidMount() {
this.props.dispatch(fetchColumnTypes());
}
componentWillUnmount() {
this.props.dispatch(setDefaults());
this.props.dispatch({
type: RESET_COLUMN_TYPE_LIST,
});
}
onTableNameChange = e => {
const { dispatch } = this.props;
dispatch(setTableName(e.target.value));
};
onTableCommentChange = e => {
const { dispatch } = this.props;
dispatch(setTableComment(e.target.value));
};
onRemoveColumn = i => {
const { dispatch } = this.props;
dispatch(removeColumn(i));
};
onColumnNameChange = (i, isNullableChecked, e) => {
const { dispatch } = this.props;
dispatch(setColName(e.target.value, i, isNullableChecked));
};
onColTypeChange = (i, value) => {
const { dispatch, columns } = this.props;
dispatch(setColType(value, i));
if (i + 1 === columns.length) {
dispatch(addCol());
}
};
onColNullableChange = (i, e) => {
const { dispatch } = this.props;
dispatch(setColNullable(e.target.checked, i));
};
onColUniqueChange = (i, numUniqueKeys, isColumnUnique, _uindex) => {
const { dispatch, uniqueKeys } = this.props;
if (isColumnUnique) {
dispatch(
setUniqueKeys([
...uniqueKeys.slice(0, _uindex),
...uniqueKeys.slice(_uindex + 1),
])
);
} else {
const newUniqueKeys = JSON.parse(JSON.stringify(uniqueKeys));
newUniqueKeys[numUniqueKeys - 1] = [i];
dispatch(setUniqueKeys([...newUniqueKeys, []]));
}
};
setColDefaultValue = (i, isNullableChecked, e) => {
const { dispatch } = this.props;
dispatch(setColDefault(e.target.value, i, isNullableChecked));
};
columnValidation() {
if (this.props.columns.length <= 0) {
// this.props.dispatch(validationError(ATLEAST_ONE_COLUMN_MSG));
@ -232,181 +283,22 @@ class AddTable extends Component {
lastError,
lastSuccess,
internalError,
dataTypes,
} = this.props;
const cols = columns.map((column, i) => {
let removeIcon;
if (i + 1 === columns.length) {
removeIcon = (
<i className={`${styles.iClickable} ${styles.fontAwosomeClose}`} />
);
} else {
removeIcon = (
<i
className={`${styles.iClickable} ${
styles.fontAwosomeClose
} fa-lg fa fa-times`}
onClick={() => {
dispatch(removeColumn(i));
}}
/>
);
const styles = require('../../../Common/TableCommon/Table.scss');
const getCreateBtnText = () => {
let createBtnText = 'Add Table';
if (ongoingRequest) {
createBtnText = 'Creating...';
} else if (lastError) {
createBtnText = 'Creating Failed. Try again';
} else if (internalError) {
createBtnText = 'Creating Failed. Try again';
} else if (lastSuccess) {
createBtnText = 'Created! Redirecting...';
}
let defValue = '';
if ('default' in column) {
defValue = column.default.value;
}
let defPlaceholder = 'default_value';
if (column.type === TIMESTAMP) {
defPlaceholder = 'example: now()';
} else if (column.type === DATE) {
defPlaceholder = '';
} else if (column.type === UUID) {
defPlaceholder = 'example: gen_random_uuid()';
}
let isColumnUnique = false;
let _uindex;
const numUniqueKeys = uniqueKeys.length;
for (let _i = numUniqueKeys - 1; _i >= 0; _i--) {
const key = uniqueKeys[_i];
if (key.length === 1) {
if (key[0] === i) {
isColumnUnique = true;
_uindex = _i;
}
}
}
const toggleUnique = () => {
if (isColumnUnique) {
dispatch(
setUniqueKeys([
...uniqueKeys.slice(0, _uindex),
...uniqueKeys.slice(_uindex + 1),
])
);
} else {
const newUniqueKeys = JSON.parse(JSON.stringify(uniqueKeys));
newUniqueKeys[numUniqueKeys - 1] = [i];
dispatch(setUniqueKeys([...newUniqueKeys, []]));
}
};
return (
<div key={i} className={`${styles.display_flex} form-group`}>
<input
type="text"
className={`${styles.input} form-control ${styles.add_mar_right}`}
value={column.name}
placeholder="column_name"
onChange={e => {
dispatch(
setColName(e.target.value, i, this.refs[`nullable${i}`].checked)
);
}}
data-test={`column-${i}`}
/>
<select
value={column.type}
className={`${styles.select} ${styles.select200} form-control ${
styles.add_pad_left
}`}
onChange={e => {
dispatch(
setColType(e.target.value, i, this.refs[`nullable${i}`].checked)
);
if (i + 1 === columns.length) {
dispatch(addCol());
}
}}
data-test={`col-type-${i}`}
>
{column.type === '' ? (
<option disabled value="">
-- type --
</option>
) : null}
{/* The below makes a set of options based of the available datatype. Refer Common/Datatypes.js for more info. */}
{dataTypes.map((datatype, index) => (
<option
value={datatype.value}
key={index}
title={datatype.description}
>
{datatype.name}
</option>
))}
</select>
{/*
{typeDescriptionDict && typeDescriptionDict[column.type] ? (
<span>
&nbsp; &nbsp;
<OverlayTrigger
placement="right"
overlay={tooltip.dataTypeDescription(
typeDescriptionDict[column.type]
)}
>
<i className="fa fa-question-circle" aria-hidden="true" />
</OverlayTrigger>{' '}
&nbsp; &nbsp;
</span>
) : null}
*/}
<input
placeholder={defPlaceholder}
type="text"
value={defValue}
className={`${styles.inputDefault} ${
styles.defaultWidth
} form-control ${styles.add_pad_left}`}
onChange={e => {
dispatch(
setColDefault(
e.target.value,
i,
this.refs[`nullable${i}`].checked
)
);
}}
data-test={`col-default-${i}`}
/>{' '}
<input
className={`${styles.inputCheckbox} form-control `}
checked={columns[i].nullable}
type="checkbox"
ref={`nullable${i}`}
onChange={e => {
dispatch(setColNullable(e.target.checked, i));
}}
data-test={`nullable-${i}`}
/>{' '}
<label>Nullable</label>
<input
className={`${styles.inputCheckbox} form-control `}
checked={isColumnUnique}
type="checkbox"
ref={`unique${i}`}
onChange={toggleUnique}
data-test={`unique-${i.toString()}`}
/>{' '}
<label>Unique</label>
{removeIcon}
</div>
);
});
let createBtnText = 'Create';
if (ongoingRequest) {
createBtnText = 'Creating...';
} else if (lastError) {
createBtnText = 'Creating Failed. Try again';
} else if (internalError) {
createBtnText = 'Creating Failed. Try again';
} else if (lastSuccess) {
createBtnText = 'Created! Redirecting...';
}
return createBtnText;
};
return (
<div
@ -424,19 +316,19 @@ class AddTable extends Component {
<div
className={`${styles.addCol} col-xs-12 ${styles.padd_left_remove}`}
>
<h4 className={styles.subheading_text}>Table Name &nbsp; &nbsp;</h4>
<input
type="text"
data-test="tableName"
placeholder="table_name"
className={`${styles.tableNameInput} form-control`}
onChange={e => {
dispatch(setTableName(e.target.value));
}}
/>
<TableName onChange={this.onTableNameChange.bind(this)} />
<hr />
<h4 className={styles.subheading_text}>Columns</h4>
{cols}
<TableColumns
uniqueKeys={uniqueKeys}
dataTypes={dataTypes}
columns={columns}
onRemoveColumn={this.onRemoveColumn}
onColumnChange={this.onColumnNameChange}
onColTypeChange={this.onColTypeChange}
onColNullableChange={this.onColNullableChange}
onColUniqueChange={this.onColUniqueChange}
setColDefaultValue={this.setColDefaultValue}
/>
<hr />
<h4 className={styles.subheading_text}>
Primary Key &nbsp; &nbsp;
@ -505,16 +397,7 @@ class AddTable extends Component {
setUniqueKeys={setUniqueKeys}
/>
<hr />
<h4 className={styles.subheading_text}>Comment &nbsp; &nbsp;</h4>
<input
type="text"
data-test="tableComment"
placeholder="comment"
className={`${styles.tableNameInput} form-control`}
onChange={e => {
dispatch(setTableComment(e.target.value));
}}
/>
<TableComment onChange={this.onTableCommentChange} />
<hr />
<Button
type="submit"
@ -523,7 +406,7 @@ class AddTable extends Component {
color="yellow"
size="sm"
>
{createBtnText}
{getCreateBtnText()}
</Button>
</div>
</div>
@ -549,6 +432,8 @@ const mapStateToProps = state => ({
...state.addTable.table,
allSchemas: state.tables.allSchemas,
currentSchema: state.tables.currentSchema,
dataTypes: state.tables.columnDataTypes,
columnDataTypeFetchErr: state.tables.columnDataTypeFetchErr,
});
const addTableConnector = connect => connect(mapStateToProps)(AddTable);

View File

@ -0,0 +1,143 @@
import React from 'react';
import PropTypes from 'prop-types';
import SearchableSelectBox from '../../../Common/SearchableSelect/SearchableSelect';
import { commonDataTypes } from '../utils';
import {
getDataOptions,
getPlaceholder,
getDefaultValue,
} from '../Common/utils';
/* Custom style object for searchable select box */
const customSelectBoxStyles = {
dropdownIndicator: {
padding: '5px',
},
singleValue: {
color: '#555555',
},
};
const TableColumn = props => {
const styles = require('../../../Common/TableCommon/Table.scss');
const {
column,
colLength,
colIndex: i,
onRemoveColumn,
onColumnChange,
onColTypeChange,
setColDefaultValue,
onColNullableChange,
onColUniqueChange,
dataTypes: restTypes,
uniqueKeys,
} = props;
let isColumnUnique = false;
let _uindex;
const numUniqueKeys = uniqueKeys.length;
for (let _i = numUniqueKeys - 1; _i >= 0; _i--) {
const key = uniqueKeys[_i];
if (key.length === 1) {
if (key[0] === i) {
isColumnUnique = true;
_uindex = _i;
}
}
}
const handleColTypeChange = selectedOption => {
onColTypeChange(selectedOption.colIdentifier, selectedOption.value);
};
const { columnDataTypes, columnTypeValueMap } = getDataOptions(
commonDataTypes,
restTypes,
i
);
const getRemoveIcon = colLen => {
let removeIcon;
if (i + 1 === colLen) {
removeIcon = <i className={`${styles.fontAwosomeClose}`} />;
} else {
removeIcon = (
<i
className={`${styles.fontAwosomeClose} fa-lg fa fa-times`}
onClick={onRemoveColumn.bind(undefined, i)}
/>
);
}
return removeIcon;
};
return (
<div key={i} className={`${styles.display_flex} form-group`}>
<input
type="text"
className={`${styles.input} form-control`}
value={column.name}
placeholder="column_name"
onChange={onColumnChange.bind(undefined, i, column.nullable || false)}
data-test={`column-${i}`}
/>
<span
className={`${styles.inputDefault} ${styles.defaultWidth}`}
data-test={`col-type-${i}`}
>
<SearchableSelectBox
options={columnDataTypes}
onChange={handleColTypeChange}
value={column.type && columnTypeValueMap[column.type]}
bsClass={`col-type-${i} add_table_column_selector`}
styleOverrides={customSelectBoxStyles}
/>
</span>
<input
placeholder={getPlaceholder(column)}
type="text"
value={getDefaultValue(column)}
className={`${styles.inputDefault} ${
styles.defaultWidth
} form-control ${styles.add_pad_left}`}
onChange={setColDefaultValue.bind(
undefined,
i,
column.nullable || false
)}
data-test={`col-default-${i}`}
/>{' '}
<input
className={`${styles.inputCheckbox} form-control `}
checked={column.nullable}
type="checkbox"
onChange={onColNullableChange.bind(undefined, i)}
data-test={`nullable-${i}`}
/>{' '}
<label>Nullable</label>
<input
className={`${styles.inputCheckbox} form-control `}
checked={isColumnUnique}
type="checkbox"
onChange={onColUniqueChange.bind(
undefined,
i,
numUniqueKeys,
isColumnUnique,
_uindex
)}
data-test={`unique-${i.toString()}`}
/>{' '}
<label>Unique</label>
{getRemoveIcon(colLength)}
</div>
);
};
TableColumn.propTypes = {
colIndex: PropTypes.number.isRequired,
column: PropTypes.object.isRequired,
colLength: PropTypes.number.isRequired,
};
export default TableColumn;

View File

@ -0,0 +1,31 @@
import React from 'react';
import PropTypes from 'prop-types';
import TableColumn from './TableColumn';
const TableColumns = props => {
const { columns } = props;
const styles = require('../../../Common/TableCommon/Table.scss');
const cols = columns.map((column, i) => {
return (
<TableColumn
key={`table_column_wrapper_${i}`}
colIndex={i}
column={column}
colLength={columns.length}
{...props}
/>
);
});
return [
<h4 key="table_columns_header" className={styles.subheading_text}>
Columns
</h4>,
<div key="table_colums_value">{cols}</div>,
];
};
TableColumns.propTypes = {
columns: PropTypes.array.isRequired,
};
export default TableColumns;

View File

@ -0,0 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';
import TextInput from '../../../Common/TextInput/TextInput';
const TableComment = ({ onChange }) => {
const styles = require('../../../Common/TableCommon/Table.scss');
return [
<h4 key="add_table_comment_header" className={styles.subheading_text}>
Comment &nbsp; &nbsp;
</h4>,
<TextInput
key="add_table_comment_element"
type="text"
placeholder="comment"
data-test="tableComment"
bsclass={`${styles.tableNameInput}`}
onChange={onChange}
/>,
];
};
TableComment.propTypes = {
onChange: PropTypes.func.isRequired,
};
export default TableComment;

View File

@ -0,0 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';
import TextInput from '../../../Common/TextInput/TextInput';
const TableNameInput = ({ onChange }) => {
const styles = require('../../../Common/TableCommon/Table.scss');
return [
<h4 key="add_table_input_header" className={styles.subheading_text}>
Table Name &nbsp; &nbsp;
</h4>,
<TextInput
key="add_table_input_element"
type="text"
placeholder="table_name"
data-test="tableName"
bsclass={`${styles.tableNameInput}`}
onChange={onChange}
/>,
];
};
TableNameInput.propTypes = {
onChange: PropTypes.func.isRequired,
};
export default TableNameInput;

View File

@ -1,82 +0,0 @@
const dataTypes = [
{
name: 'Integer',
value: 'integer',
description: 'signed four-byte integer',
hasuraDatatype: 'integer',
},
{
name: 'Integer (auto-increment)',
value: 'serial',
description: 'autoincrementing four-byte integer',
hasuraDatatype: null,
},
{
name: 'UUID',
value: 'uuid',
description: 'universal unique identifier',
hasuraDatatype: 'uuid',
},
{
name: 'Big Integer',
value: 'bigint',
description: 'signed eight-byte integer',
hasuraDatatype: 'bigint',
},
{
name: 'Big Integer (auto-increment)',
value: 'bigserial',
description: 'autoincrementing eight-byte integer',
hasuraDatatype: null,
},
{
name: 'Text',
value: 'text',
description: 'variable-length character string',
hasuraDatatype: 'text',
},
{
name: 'Numeric',
value: 'numeric',
description: 'exact numeric of selected precision',
hasuraDatatype: 'numeric',
},
{
name: 'Date',
value: 'date',
description: 'calendar date (year, month, day)',
hasuraDatatype: 'date',
},
{
name: 'Timestamp',
value: 'timestamp with time zone',
description: 'date and time, including time zone',
hasuraDatatype: 'timestamp with time zone',
},
{
name: 'Time',
value: 'time with time zone',
description: 'time of day (no time zone)',
hasuraDatatype: 'time with time zone',
},
{
name: 'Boolean',
value: 'boolean',
description: 'logical Boolean (true/false)',
hasuraDatatype: 'boolean',
},
{
name: 'JSON',
value: 'json',
description: 'textual JSON data',
hasuraDatatype: 'json',
},
{
name: 'JSONB',
value: 'jsonb',
description: 'binary format JSON data',
hasuraDatatype: 'jsonb',
},
];
export default dataTypes;

View File

@ -0,0 +1,34 @@
const pgCategoryCode = {
A: 'Array types',
B: 'Boolean types',
C: 'Composite types',
D: 'Date/time types',
E: 'Enum types',
G: 'Geometric types',
I: 'Network address types',
N: 'Numeric types',
P: 'Pseudo-types',
R: 'Range types',
S: 'String types',
T: 'Timespan types',
U: 'User-defined types',
V: 'Bit-string types',
X: 'unknown type',
};
const topCategory = ['N', 'S', 'B', 'D', 'T'];
const restCategory = [
...Object.keys(pgCategoryCode).filter(p => topCategory.indexOf(p) === -1),
];
const aggCategory = [...topCategory, ...restCategory];
const serialTypes = [
'serial,bigserial,smallserial',
'serial,bigserial,smallserial',
'autoincrementing integer,large autoincrementing integer, small autoincrementing integer',
'N',
];
export { pgCategoryCode, aggCategory, serialTypes };

View File

@ -0,0 +1,103 @@
import { aggCategory, pgCategoryCode } from './PgInfo';
const getDataTypeInfo = (row, categoryInfo, colId) => {
const columnTypeValueMap = {};
// Splits comma seperated type names
const typInfo = row[0].split(',');
// Splits comma seperated type display names
const typDisplayName = row[1].split(',');
// Splits comma seperated type descriptions
const typDescription = row[2].split(':');
// Create option object for every valid type
const currTypeObj = typInfo.map((t, i) => {
const optObj = {
value: t,
label: typDisplayName[i],
key: `${categoryInfo}_${i}`,
colIdentifier: colId,
description: typDescription[i],
};
// Memoizing option for later use
columnTypeValueMap[t] = optObj;
return optObj;
});
return { typInfo: currTypeObj, typValueMap: columnTypeValueMap };
};
/*
* Input arguments:
* dataTypes -> Frequently used types
* , restTypes -> Information queried from database
* , identifier -> Identifies where this column to be tracked
* Output:
* 1) Type -> grouped option
* 2) returns array of `grouped` options
* */
const getDataOptions = (commonDataTypes, restTypes, identifier) => {
let columnTypeValueMap = {};
const columnDataTypes = [];
const mainOpts = [];
commonDataTypes.forEach((d, dKey) => {
mainOpts.push({
value: d.value,
label: d.name,
description: d.description,
key: dKey,
colIdentifier: identifier,
});
columnTypeValueMap[d.value] = mainOpts[mainOpts.length - 1];
});
columnDataTypes.push({
label: 'Frequently Used Types',
options: mainOpts,
});
/*
* restTypes will be a list of arrays,
* each array will have
* [
* "Types available in a particular group",
* "Display Name of a type",
* "Description of a type",
* "Category the particular type belongs to"
* ]
* */
aggCategory.forEach(category => {
const categoryRow = restTypes.filter(r => r[3] === category);
if (categoryRow.length > 0) {
const { typInfo, typValueMap } = getDataTypeInfo(
categoryRow[0],
pgCategoryCode[category],
identifier
);
columnTypeValueMap = { ...columnTypeValueMap, ...typValueMap };
columnDataTypes.push({
label: pgCategoryCode[category],
options: typInfo,
});
}
});
return {
columnTypeValueMap,
columnDataTypes,
};
};
const getPlaceholder = column => {
switch (column.type) {
case 'timestamptz':
return 'example: now()';
case 'date':
return '';
case 'uuid':
return 'example: gen_random_uuid()';
default:
return 'default_value';
}
};
const getDefaultValue = column => {
return ('default' in column && column.default.value) || '';
};
export { getDataOptions, getPlaceholder, getDefaultValue, getDataTypeInfo };

View File

@ -21,6 +21,8 @@ import {
} from '../Metadata/Actions';
import globals from '../../../Globals';
import { fetchColumnTypesQuery } from './utils';
import { SERVER_CONSOLE_MODE } from '../../../constants';
const SET_TABLE = 'Data/SET_TABLE';
@ -43,6 +45,10 @@ const UPDATE_REMOTE_SCHEMA_MANUAL_REL = 'Data/UPDATE_SCHEMA_MANUAL_REL';
const SET_CONSISTENT_SCHEMA = 'Data/SET_CONSISTENT_SCHEMA';
const SET_CONSISTENT_FUNCTIONS = 'Data/SET_CONSISTENT_FUNCTIONS';
const FETCH_COLUMN_TYPE_LIST = 'Data/FETCH_COLUMN_TYPE_LIST';
const FETCH_COLUMN_TYPE_LIST_FAIL = 'Data/FETCH_COLUMN_TYPE_LIST_FAIL';
const RESET_COLUMN_TYPE_LIST = 'Data/RESET_COLUMN_TYPE_LIST';
const MAKE_REQUEST = 'ModifyTable/MAKE_REQUEST';
const REQUEST_SUCCESS = 'ModifyTable/REQUEST_SUCCESS';
const REQUEST_ERROR = 'ModifyTable/REQUEST_ERROR';
@ -655,6 +661,48 @@ const fetchTableListBySchema = (schemaName, successAction, errorAction) => (
);
};
/* */
const fetchColumnTypes = () => {
return (dispatch, getState) => {
const url = Endpoints.getSchema;
const reqQuery = {
type: 'run_sql',
args: {
sql: fetchColumnTypesQuery,
},
};
const options = {
credentials: globalCookiePolicy,
method: 'POST',
headers: dataHeaders(getState),
body: JSON.stringify(reqQuery),
};
return dispatch(requestAction(url, options)).then(
data => {
return dispatch({
type: FETCH_COLUMN_TYPE_LIST,
data: data.result.slice(1),
});
},
error => {
dispatch(
showErrorNotification(
'Error fetching column types',
'Kindly reach out to us in case you face this issue again',
error,
error
)
);
return dispatch({
type: FETCH_COLUMN_TYPE_LIST_FAIL,
data: error,
});
}
);
};
};
/* */
/* ******************************************************* */
const dataReducer = (state = defaultState, action) => {
// eslint-disable-line no-unused-vars
@ -788,6 +836,25 @@ const dataReducer = (state = defaultState, action) => {
},
},
};
case FETCH_COLUMN_TYPE_LIST:
return {
...state,
columnDataTypes: action.data,
columnDataTypeFetchErr: 'Error fetching data',
};
case FETCH_COLUMN_TYPE_LIST_FAIL:
return {
...state,
columnDataTypes: [],
columnDataTypeFetchErr: action.data,
};
case RESET_COLUMN_TYPE_LIST:
return {
...state,
columnDataTypes: [...defaultState.columnDataTypes],
columnDataTypeFetchErr: defaultState.columnDataTypes,
};
default:
return state;
}
@ -823,4 +890,7 @@ export {
initQueries,
setConsistentSchema,
setConsistentFunctions,
//
fetchColumnTypes,
RESET_COLUMN_TYPE_LIST,
};

View File

@ -125,9 +125,13 @@ const defaultModifyState = {
viewDefinition: null,
viewDefinitionError: null,
tableCommentEdit: { enabled: false, editedValue: null },
alterColumnOptions: [], // Store supported implicit column -> column casts
alterColumnOptionsFetchErr: null,
};
const defaultState = {
columnDataTypes: [], // To store list of column types supported by postgres
columnDataTypeFetchErr: null,
currentTable: null,
view: { ...defaultViewState },
modify: { ...defaultModifyState },

View File

@ -13,7 +13,7 @@ const getSQLValue = value => {
return sqlValue.replace(/['"]+/g, '');
};
const parseCreateSQL = (sql) => {
const parseCreateSQL = sql => {
const _objects = [];
const regExp = createSQLRegex;

View File

@ -207,13 +207,7 @@ const ViewRows = ({
return (
<Button
className={
styles.tableActionBtn +
' ' +
styles.add_mar_right_small +
' ' +
styles.remove_margin_right
}
className={styles.add_mar_right_small}
color="white"
size="xs"
onClick={disabled ? disabledOnClick : handleClick}

View File

@ -18,6 +18,8 @@ import {
EDIT_COLUMN,
SET_PRIMARY_KEYS,
SET_FOREIGN_KEYS,
FETCH_COLUMN_TYPE_CASTS,
FETCH_COLUMN_TYPE_CASTS_FAIL,
RESET,
SET_UNIQUE_KEYS,
} from '../TableModify/ModifyActions';
@ -552,6 +554,21 @@ const modifyReducer = (tableName, schemas, modifyStateOrig, action) => {
...modifyState,
fkModify: action.fks,
};
case FETCH_COLUMN_TYPE_CASTS:
return {
...modifyState,
alterColumnOptions: action.data,
alterColumnOptionsFetchErr: null,
};
case FETCH_COLUMN_TYPE_CASTS_FAIL:
return {
...modifyState,
alterColumnOptions: [],
alterColumnOptionsFetchErr: action.data,
};
case SET_UNIQUE_KEYS:
return {
...modifyState,

View File

@ -1,7 +1,12 @@
import React, { useState } from 'react';
import { showErrorNotification } from '../../Common/Notification';
import gqlPattern, { gqlColumnErrorNotif } from '../Common/GraphQLValidation';
import dataTypes from '../Common/DataTypes';
import { commonDataTypes } from '../utils';
import SearchableSelectBox from '../../../Common/SearchableSelect/SearchableSelect';
import { getDataOptions } from '../Common/utils';
import Button from '../../../Common/Button/Button';
import { addColSql } from '../TableModify/ModifyActions';
@ -67,8 +72,8 @@ const useColumnEditor = (dispatch, tableName) => {
},
colType: {
value: colType,
onChange: e => {
setColumnState({ ...columnState, colType: e.target.value });
onChange: selected => {
setColumnState({ ...columnState, colType: selected.value });
},
},
colNull: {
@ -93,13 +98,7 @@ const useColumnEditor = (dispatch, tableName) => {
};
};
const alterTypeOptions = dataTypes.map((datatype, index) => (
<option value={datatype.value} key={index} title={datatype.description}>
{datatype.name}
</option>
));
const ColumnCreator = ({ dispatch, tableName }) => {
const ColumnCreator = ({ dispatch, tableName, dataTypes: restTypes = [] }) => {
const {
colName,
colType,
@ -109,6 +108,30 @@ const ColumnCreator = ({ dispatch, tableName }) => {
onSubmit,
} = useColumnEditor(dispatch, tableName);
const { columnDataTypes, columnTypeValueMap } = getDataOptions(
commonDataTypes,
restTypes,
0
);
const customSelectBoxStyles = {
container: {
width: '186px',
},
dropdownIndicator: {
padding: '5px',
},
placeholder: {
top: '44%',
fontSize: '12px',
},
singleValue: {
fontSize: '12px',
top: '44%',
color: '#555555',
},
};
return (
<div className={styles.activeEdit}>
<form
@ -122,17 +145,15 @@ const ColumnCreator = ({ dispatch, tableName }) => {
data-test="column-name"
{...colName}
/>
<select
className={`${styles.select} input-sm form-control`}
data-test="data-type"
{...colType}
>
<option disabled value="">
-- type --
</option>
{alterTypeOptions}
</select>
<span className={`${styles.select}`} data-test="col-type-0">
<SearchableSelectBox
options={columnDataTypes}
onChange={colType.onChange}
value={colType.value && columnTypeValueMap[colType.value]}
bsClass={`col-type-${0} modify_select`}
styleOverrides={customSelectBoxStyles}
/>
</span>
<input
type="checkbox"
className={`${styles.input} ${styles.nullable} input-sm form-control`}

View File

@ -1,6 +1,8 @@
import React, { useEffect } from 'react';
import dataTypes from '../Common/DataTypes';
import { convertListToDictUsingKV } from '../../../../utils/data';
import SearchableSelectBox from '../../../Common/SearchableSelect/SearchableSelect';
/*
import {
INTEGER,
SERIAL,
@ -11,9 +13,10 @@ import {
JSONB,
TIMESTAMP,
TIME,
NUMERIC,
TEXT,
} from '../utils';
} from '../../../../constants';
*/
import { getValidAlterOptions } from './utils';
const ColumnEditor = ({
onSubmit,
@ -22,6 +25,7 @@ const ColumnEditor = ({
columnProperties,
selectedProperties,
editColumn,
alterTypeOptions,
}) => {
const colName = columnProperties.name;
@ -37,89 +41,40 @@ const ColumnEditor = ({
}
}, [columnComment]);
// filter the datatypes where hasuraDatatype === null
const typeMap = convertListToDictUsingKV(
'hasuraDatatype',
'value',
dataTypes.filter(dataType => dataType.hasuraDatatype)
);
const getAlternateTypeOptions = columntype => {
const generateOptions = datatypeOptions => {
const options = [];
dataTypes.forEach(datatype => {
if (datatypeOptions.includes(datatype.value)) {
options.push(
<option
value={datatype.value}
key={datatype.name}
title={datatype.description}
>
{datatype.name}
</option>
);
}
});
let finalDefaultValue = typeMap[columnProperties.type];
if (!finalDefaultValue) {
finalDefaultValue = columnProperties.type;
options.push(
<option value={finalDefaultValue} key={finalDefaultValue}>
{finalDefaultValue}
</option>
);
}
return options;
};
const integerOptions = [INTEGER, SERIAL, BIGINT, BIGSERIAL, NUMERIC, TEXT];
const bigintOptions = [BIGINT, BIGSERIAL, NUMERIC, TEXT];
const uuidOptions = [UUID, TEXT];
const jsonOptions = [JSON, JSONB, TEXT];
const timestampOptions = [TIMESTAMP, TEXT];
const timeOptions = [TIME, TEXT];
switch (columntype) {
case INTEGER:
return generateOptions(integerOptions);
case SERIAL:
return generateOptions(integerOptions);
case BIGINT:
return generateOptions(bigintOptions);
case BIGSERIAL:
return generateOptions(bigintOptions);
case UUID:
return generateOptions(uuidOptions);
case JSONDTYPE:
return generateOptions(jsonOptions);
case JSONB:
return generateOptions(jsonOptions);
case TIMESTAMP:
return generateOptions(timestampOptions);
case TIME:
return generateOptions(timeOptions);
default:
return generateOptions([columntype, TEXT]);
}
const getColumnType = () => {
return (
colName in selectedProperties &&
'type' in selectedProperties[colName] &&
selectedProperties[colName].type
);
};
const columnTypePG = getColumnType();
const customSelectBoxStyles = {
dropdownIndicator: {
padding: '5px',
},
placeholder: {
top: '44%',
fontSize: '12px',
},
singleValue: {
fontSize: '12px',
top: '44%',
color: '#555555',
},
};
const { alterOptions, alterOptionsValueMap } = getValidAlterOptions(
alterTypeOptions,
colName
);
const updateColumnName = e => {
dispatch(editColumn(colName, 'name', e.target.value));
};
const updateColumnType = e => {
dispatch(editColumn(colName, 'type', e.target.value));
const updateColumnType = selected => {
dispatch(editColumn(colName, 'type', selected.value));
};
const updateColumnDef = e => {
dispatch(editColumn(colName, 'default', e.target.value));
@ -152,14 +107,13 @@ const ColumnEditor = ({
<div className={`${styles.display_flex} form-group`}>
<label className="col-xs-2">Type</label>
<div className="col-xs-6">
<select
value={selectedProperties[colName].type}
<SearchableSelectBox
options={alterOptions}
onChange={updateColumnType}
className="input-sm form-control"
disabled={columnProperties.pkConstraint}
>
{getAlternateTypeOptions(columnProperties.type)}
</select>
value={columnTypePG && alterOptionsValueMap[columnTypePG]}
bsClass={`col-type-${0} modify_select`}
styleOverrides={customSelectBoxStyles}
/>
</div>
</div>
<div className={`${styles.display_flex} form-group`}>

View File

@ -20,6 +20,8 @@ const ColumnEditorList = ({
columnEdit,
dispatch,
columnComments,
//
validTypeCasts,
}) => {
const tableName = tableSchema.table_name;
@ -39,6 +41,9 @@ const ColumnEditorList = ({
const columns = tableSchema.columns.sort(ordinalColSort);
/*
* col.udt_name contains internal representation of the data type
* */
return columns.map((col, i) => {
const colName = col.column_name;
@ -46,7 +51,9 @@ const ColumnEditorList = ({
name: colName,
tableName: col.table_name,
schemaName: col.table_schema,
type: col.data_type !== 'USER-DEFINED' ? col.data_type : col.udt_name,
display_type_name:
col.data_type !== 'USER-DEFINED' ? col.data_type : col.udt_name,
type: col.udt_name,
isNullable: col.is_nullable === 'YES',
pkConstraint: columnPKConstraints[colName],
isUnique: columnUniqueConstraints[colName] ? true : false,
@ -79,7 +86,7 @@ const ColumnEditorList = ({
const keyProperties = () => {
const propertiesList = [];
propertiesList.push(columnProperties.type);
propertiesList.push(columnProperties.display_type_name);
if (columnProperties.pkConstraint) {
propertiesList.push('primary key');
@ -121,6 +128,7 @@ const ColumnEditorList = ({
const colEditorExpanded = () => {
return (
<ColumnEditor
alterTypeOptions={validTypeCasts[col.udt_name]}
column={col}
onSubmit={onSubmit}
onDelete={safeOnDelete}

View File

@ -26,6 +26,8 @@ import {
getUniqueConstraintName,
} from '../Common/ReusableComponents/utils';
import { fetchColumnCastsQuery, convertArrayToJson } from './utils';
const DELETE_PK_WARNING =
'Without a Primary key there is no way to uniquely identify a row of a table. Are you sure?';
@ -50,6 +52,8 @@ const SET_FOREIGN_KEYS = 'ModifyTable/SET_FOREIGN_KEYS';
const SAVE_FOREIGN_KEY = 'ModifyTable/SAVE_FOREIGN_KEY';
const REMOVE_FOREIGN_KEY = 'ModifyTable/REMOVE_FOREIGN_KEY';
const FETCH_COLUMN_TYPE_CASTS = 'ModifyTable/FETCH_COLUMN_TYPE_CASTS';
const FETCH_COLUMN_TYPE_CASTS_FAIL = 'ModifyTable/FETCH_COLUMN_TYPE_CASTS_FAIL';
const SET_UNIQUE_KEYS = 'ModifyTable/SET_UNIQUE_KEYS';
const SAVE_UNIQUE_KEY = 'ModifyTable/SAVE_UNIQUE_KEY';
const REMOVE_UNIQUE_KEY = 'ModifyTable/REMOVE_UNIQUE_KEY';
@ -1616,6 +1620,46 @@ const saveColumnChangesSql = (colName, column) => {
};
};
const fetchColumnCasts = () => {
return (dispatch, getState) => {
const url = Endpoints.getSchema;
const reqQuery = {
type: 'run_sql',
args: {
sql: fetchColumnCastsQuery,
},
};
const options = {
credentials: globalCookiePolicy,
method: 'POST',
headers: dataHeaders(getState),
body: JSON.stringify(reqQuery),
};
return dispatch(requestAction(url, options)).then(
data => {
return dispatch({
type: FETCH_COLUMN_TYPE_CASTS,
data: convertArrayToJson(data.result.slice(1)),
});
},
error => {
dispatch(
showErrorNotification(
'Error fetching column casts information',
'Kindly reach out to us in case you face this issue again',
error,
error
)
);
return dispatch({
type: FETCH_COLUMN_TYPE_CASTS_FAIL,
data: error,
});
}
);
};
};
const removeUniqueKey = (index, tableName, existingConstraints, callback) => {
return (dispatch, getState) => {
dispatch({ type: REMOVE_UNIQUE_KEY });
@ -1802,6 +1846,8 @@ const saveUniqueKey = (
};
export {
FETCH_COLUMN_TYPE_CASTS,
FETCH_COLUMN_TYPE_CASTS_FAIL,
VIEW_DEF_REQUEST_SUCCESS,
VIEW_DEF_REQUEST_ERROR,
SET_COLUMN_EDIT,
@ -1843,6 +1889,7 @@ export {
setForeignKeys,
saveForeignKeys,
removeForeignKey,
fetchColumnCasts,
setUniqueKeys,
removeUniqueKey,
saveUniqueKey,

View File

@ -1,13 +1,20 @@
import PropTypes from 'prop-types';
import React from 'react';
import TableHeader from '../TableCommon/TableHeader';
import {
deleteTableSql,
untrackTableSql,
RESET,
fetchColumnCasts,
setUniqueKeys,
} from '../TableModify/ModifyActions';
import { setTable, fetchTableComment } from '../DataActions';
import {
setTable,
fetchTableComment,
fetchColumnTypes,
RESET_COLUMN_TYPE_LIST,
} from '../DataActions';
import Button from '../../../Common/Button/Button';
import ColumnEditorList from './ColumnEditorList';
import ColumnCreator from './ColumnCreator';
@ -23,8 +30,14 @@ class ModifyTable extends React.Component {
dispatch({ type: RESET });
dispatch(setTable(this.props.tableName));
dispatch(fetchTableComment(this.props.tableName));
dispatch(fetchColumnTypes());
dispatch(fetchColumnCasts());
}
componentWillUnmount() {
this.props.dispatch({
type: RESET_COLUMN_TYPE_LIST,
});
}
render() {
const {
tableName,
@ -38,8 +51,11 @@ class ModifyTable extends React.Component {
columnEdit,
pkModify,
fkModify,
dataTypes,
validTypeCasts,
uniqueKeyModify,
} = this.props;
const tableSchema = allSchemas.find(t => t.table_name === tableName);
const untrackBtn = (
@ -77,6 +93,7 @@ class ModifyTable extends React.Component {
</Button>
);
// if (tableSchema.primary_key.columns > 0) {}
return (
<div className={`${styles.container} container-fluid`}>
<TableHeader
@ -102,6 +119,7 @@ class ModifyTable extends React.Component {
/>
<h4 className={styles.subheading_text}>Columns</h4>
<ColumnEditorList
validTypeCasts={validTypeCasts}
tableSchema={tableSchema}
columnEdit={columnEdit}
columnComments={columnComments}
@ -110,7 +128,11 @@ class ModifyTable extends React.Component {
/>
<hr />
<h4 className={styles.subheading_text}>Add a new column</h4>
<ColumnCreator dispatch={dispatch} tableName={tableName} />
<ColumnCreator
dispatch={dispatch}
tableName={tableName}
dataTypes={dataTypes}
/>
<hr />
<h4 className={styles.subheading_text}>Primary Key</h4>
<PrimaryKeyEditor
@ -182,6 +204,9 @@ const mapStateToProps = (state, ownProps) => ({
columnEdit: state.tables.modify.columnEdit,
pkModify: state.tables.modify.pkModify,
fkModify: state.tables.modify.fkModify,
dataTypes: state.tables.columnDataTypes,
validTypeCasts: state.tables.modify.alterColumnOptions,
columnDataTypeFetchErr: state.tables.columnDataTypeFetchErr,
...state.tables.modify,
});

View File

@ -47,6 +47,11 @@
margin-right: 10px;
}
.column_type_select {
width: 200px;
font-size: 12px;
}
.modifyMinWidth {
min-width: 735px;
}
@ -250,7 +255,7 @@ hr {
background: #fff;
border: 1px solid #ccc;
margin-bottom: 15px;
overflow: auto;
// overflow: auto;
.editPermsHeading {
font-weight: bold;

View File

@ -0,0 +1,62 @@
import { getDataTypeInfo } from '../Common/utils';
const convertArrayToJson = (arr, keyIndex = 0) => {
const converted = {};
arr.forEach(a => {
converted[a[keyIndex]] = a;
});
return converted;
};
const getValidAlterOptions = (alterTypeOptions, colName) => {
const { typInfo: currentInfo, typValueMap: currentMap } = getDataTypeInfo(
alterTypeOptions.slice(0, 3),
colName,
0
);
const {
typInfo: validOptions,
typValueMap: validOptionsMap,
} = getDataTypeInfo(alterTypeOptions.slice(3, 6), colName, 0);
const allInfo = [...currentInfo, ...validOptions];
const allOptionsMap = {
...validOptionsMap,
...currentMap,
};
return {
alterOptions: allInfo,
alterOptionsValueMap: allOptionsMap,
};
};
const fetchColumnCastsQuery = `
SELECT ts.typname AS "Source Type",
pg_catalog.format_type(castsource, NULL) AS "Source Info",
pg_catalog.obj_description(castsource, 'pg_type') as "Source Descriptions",
string_agg(tt.typname, ',') AS "Target Type",
string_agg(pg_catalog.format_type(casttarget, NULL), ',') AS "Target Info",
string_agg(pg_catalog.obj_description(casttarget, 'pg_type'), ':') as "Target Descriptions",
string_agg(CASE WHEN castfunc = 0 THEN '(binary coercible)'
ELSE p.proname
END, ',') as "Function"
FROM pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p
ON c.castfunc = p.oid
LEFT JOIN pg_catalog.pg_type ts
ON c.castsource = ts.oid
LEFT JOIN pg_catalog.pg_namespace ns
ON ns.oid = ts.typnamespace
LEFT JOIN pg_catalog.pg_type tt
ON c.casttarget = tt.oid
LEFT JOIN pg_catalog.pg_namespace nt
ON nt.oid = tt.typnamespace
WHERE ( (true AND pg_catalog.pg_type_is_visible(ts.oid)
) OR (true AND pg_catalog.pg_type_is_visible(tt.oid)
) ) AND (c.castcontext != 'e') AND ts.typname != tt.typname
GROUP BY ts.typname, castsource
ORDER BY 1, 2;
`;
export { convertArrayToJson, getValidAlterOptions, fetchColumnCastsQuery };

View File

@ -51,11 +51,7 @@ const permChangeTypes = {
delete: 'delete',
};
const permOpenEdit = (
tableSchema,
role,
query,
) => ({
const permOpenEdit = (tableSchema, role, query) => ({
type: PERM_OPEN_EDIT,
tableSchema,
role,
@ -118,11 +114,7 @@ const getFilterKey = query => {
return query === 'insert' ? 'check' : 'filter';
};
const getBasePermissionsState = (
tableSchema,
role,
query,
) => {
const getBasePermissionsState = (tableSchema, role, query) => {
const _permissions = JSON.parse(JSON.stringify(defaultPermissionsState));
_permissions.table = tableSchema.table_name;
@ -140,9 +132,7 @@ const getBasePermissionsState = (
if (q === 'insert' || q === 'update') {
// If set is an object
if (!_permissions[q].columns) {
_permissions[q].columns = tableSchema.columns.map(
c => c.column_name
);
_permissions[q].columns = tableSchema.columns.map(c => c.column_name);
}
if ('set' in _permissions[q]) {
if (

View File

@ -638,9 +638,9 @@ class PermissionBuilder extends React.Component {
const columnOptions = tableColumns.concat(tableRelationships);
const operatorOptions = columnOptions
const operatorOptions = boolOperators
.concat(['---'])
.concat(boolOperators);
.concat(columnOptions);
const _boolExpKey = renderSelect(
dispatchOperationSelect,

View File

@ -179,3 +179,93 @@ export const getTableName = t => {
}
return '';
};
export const commonDataTypes = [
{
name: 'Integer',
value: 'integer',
description: 'signed four-byte integer',
hasuraDatatype: 'integer',
},
{
name: 'Integer (auto-increment)',
value: 'serial',
description: 'autoincrementing four-byte integer',
hasuraDatatype: null,
},
{
name: 'UUID',
value: 'uuid',
description: 'universal unique identifier',
hasuraDatatype: 'uuid',
},
{
name: 'Big Integer',
value: 'bigint',
description: 'signed eight-byte integer',
hasuraDatatype: 'bigint',
},
{
name: 'Big Integer (auto-increment)',
value: 'bigserial',
description: 'autoincrementing eight-byte integer',
hasuraDatatype: null,
},
{
name: 'Text',
value: 'text',
description: 'variable-length character string',
hasuraDatatype: 'text',
},
{
name: 'Numeric',
value: 'numeric',
description: 'exact numeric of selected precision',
hasuraDatatype: 'numeric',
},
{
name: 'Date',
value: 'date',
description: 'calendar date (year, month, day)',
hasuraDatatype: 'date',
},
{
name: 'Timestamp',
value: 'timestamptz',
description: 'date and time, including time zone',
hasuraDatatype: 'timestamp with time zone',
},
{
name: 'Time',
value: 'timetz',
description: 'time of day (no time zone)',
hasuraDatatype: 'time with time zone',
},
{
name: 'Boolean',
value: 'boolean',
description: 'logical Boolean (true/false)',
hasuraDatatype: 'boolean',
},
{
name: 'JSONB',
value: 'jsonb',
description: 'binary format JSON data',
hasuraDatatype: 'jsonb',
},
];
export const fetchColumnTypesQuery = `
SELECT
string_agg(t.typname, ',') as "Type Name",
string_agg(pg_catalog.format_type(t.oid, NULL), ',') as "Display Name",
string_agg(pg_catalog.obj_description(t.oid, 'pg_type'), ':') as "Descriptions",
t.typcategory
FROM pg_catalog.pg_type t
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
WHERE (t.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid))
AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)
AND pg_catalog.pg_type_is_visible(t.oid)
AND t.typname != 'unknown'
AND t.typcategory != 'P'
GROUP BY t.typcategory;`;

View File

@ -81,6 +81,11 @@ class WebhookEditor extends React.Component {
}
testId="webhook"
/>
<br />
<small>
Note: Specifying the webhook URL via an environmental variable is
recommended if you have different URLs for multiple environments.
</small>
</div>
);

View File

@ -266,7 +266,8 @@ const ViewRows = ({
// Insert cells corresponding to all rows
invocationColumns.forEach(col => {
const getCellContent = () => {
let conditionalClassname = styles.tableCellCenterAlignedOverflow;
let conditionalClassname =
styles.tableCellCenterAlignedOverflow;
const cellIndex = `${curTriggerName}-${col}-${rowIndex}`;
if (expandedRow === cellIndex) {
conditionalClassname = styles.tableCellExpanded;

View File

@ -288,7 +288,8 @@ const ViewRows = ({
// Insert cells corresponding to all rows
invocationColumns.forEach(col => {
const getCellContent = () => {
let conditionalClassname = styles.tableCellCenterAlignedOverflow;
let conditionalClassname =
styles.tableCellCenterAlignedOverflow;
const cellIndex = `${curTriggerName}-${col}-${rowIndex}`;
if (expandedRow === cellIndex) {
conditionalClassname = styles.tableCellExpanded;

View File

@ -93,7 +93,9 @@ class RedeliverEvent extends Component {
r.status === 200 ? (
<i
className={
styles.invocationSuccess + ' fa fa-check invocationsSuccess ' + styles.tabletdCenter
styles.invocationSuccess +
' fa fa-check invocationsSuccess ' +
styles.tabletdCenter
}
/>
) : (
@ -212,7 +214,13 @@ class RedeliverEvent extends Component {
<Modal.Body>
<div className="content-fluid">
<div>
<div className={styles.padd_left_remove + ' col-md-12 ' + styles.padd_right_remove} >
<div
className={
styles.padd_left_remove +
' col-md-12 ' +
styles.padd_right_remove
}
>
<div className={styles.add_mar_bottom}>
Event ID - {log.redeliverEventId}
<Button

View File

@ -7,6 +7,22 @@
.hljs{display:block;overflow-x:auto;padding:0.5em;background:#F0F0F0}.hljs,.hljs-subst{color:#444}.hljs-comment{color:#888888}.hljs-keyword,.hljs-attribute,.hljs-selector-tag,.hljs-meta-keyword,.hljs-doctag,.hljs-name{font-weight:bold}.hljs-type,.hljs-string,.hljs-number,.hljs-selector-id,.hljs-selector-class,.hljs-quote,.hljs-template-tag,.hljs-deletion{color:#880000}.hljs-title,.hljs-section{color:#880000;font-weight:bold}.hljs-regexp,.hljs-symbol,.hljs-variable,.hljs-template-variable,.hljs-link,.hljs-selector-attr,.hljs-selector-pseudo{color:#BC6060}.hljs-literal{color:#78A960}.hljs-built_in,.hljs-bullet,.hljs-code,.hljs-addition{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold}
/* */
// react select overrides: start
.modify_select__control {
min-height: 30px !important;
max-height: 30px !important;
}
.add_table_column_selector__control {
min-height: 34px !important;
max-height: 34px !important;
}
.react-selectable__single-value, .react-selectable__placeholder, .react-selectable__indicator.react-selectable__dropdown-indicator {
margin-top: -2px;
}
// react select overrides: end
input[type="radio"], input[type="checkbox"] {
margin: 0 5px 0px 0px;
}