close #57 fix messaging, expand track all relations (#88)

This commit is contained in:
Praveen Durairaj 2018-07-11 10:28:52 +05:30 committed by Shahidh K Muhammed
parent 83ab85fb04
commit 113b6a9bb5
16 changed files with 283 additions and 58 deletions

View File

@ -39,7 +39,6 @@
}
.cursorNotAllowed {
cursor: not-allowed;
pointer-events: none;
}
.apiExplorerWrapper
{

View File

@ -27,7 +27,7 @@ dataApisContent.push({
details: {
title: 'GraphQL API',
description:
'GraphQL API for CRUD operations on tables/views in your database',
'GraphQL API for CRUD operations on tables & views in your database',
category: 'data',
},
request: {

View File

@ -380,6 +380,10 @@ input {
{
padding-left: 5px;
}
.padd_small_right
{
padding-right: 5px;
}
.border_bottom
{
border-bottom: 1px solid #e7e7e7;
@ -495,6 +499,10 @@ code
{
-webkit-padding-start: 15px;
}
.add_pad_bottom
{
padding-bottom: 20px;
}
.add_padd_bottom
{
padding-bottom: 25px;
@ -985,6 +993,7 @@ code
.graphQLHeight {
height: 100vh;
margin-bottom: 50px;
}
/* container height subtracting top header and bottom scroll bar */

View File

@ -31,11 +31,18 @@ const Main = ({ children, location, migrationModeProgress, currentSchema }) => {
let accessKeyHtml = null;
if (globals.accessKey === '' || globals.accessKey === null) {
accessKeyHtml = (
<a href="https://docs.hasura.io/1.0/graphql/manual/deployment/production.html">
<button className={'btn btn-danger ' + styles.add_mar_right}>
Secure your endpoint
</button>
</a>
<OverlayTrigger placement="left" overlay={tooltip.secureEndpoint}>
<a href="https://docs.hasura.io/1.0/graphql/manual/deployment/production.html">
<button className={'btn btn-danger ' + styles.add_mar_right}>
<i
className={
styles.padd_small_right + ' fa fa-exclamation-triangle'
}
/>
Secure your endpoint
</button>
</a>
</OverlayTrigger>
);
}
@ -75,7 +82,7 @@ const Main = ({ children, location, migrationModeProgress, currentSchema }) => {
aria-hidden="true"
/>
</div>
<p>API Explorer</p>
<p>GraphiQL</p>
</Link>
</li>
</OverlayTrigger>

View File

@ -6,5 +6,11 @@ export const data = (
);
export const apiexplorer = (
<Tooltip id="tooltip-api-explorer">Test the Hasura APIs</Tooltip>
<Tooltip id="tooltip-api-explorer">Test the GraphQL APIs</Tooltip>
);
export const secureEndpoint = (
<Tooltip id="tooltip-secure-endpoint">
This graphql endpoint is public and you should add an access key
</Tooltip>
);

View File

@ -456,16 +456,7 @@ class AddTable extends Component {
<div
className={`${styles.addCol} col-xs-12 ${styles.padd_left_remove}`}
>
<h4 className={styles.subheading_text}>
Table name: &nbsp; &nbsp;
<OverlayTrigger
placement="right"
overlay={tooltip.databaseNamingScheme}
>
<i className="fa fa-question-circle" aria-hidden="true" />
</OverlayTrigger>{' '}
&nbsp; &nbsp;
</h4>
<h4 className={styles.subheading_text}>Table name &nbsp; &nbsp;</h4>
<input
type="text"
data-test="tableName"
@ -480,7 +471,7 @@ class AddTable extends Component {
{cols}
<hr />
<h4 className={styles.subheading_text}>
Primary Key: &nbsp; &nbsp;
Primary Key &nbsp; &nbsp;
<OverlayTrigger
placement="right"
overlay={tooltip.primaryKeyDescription}

View File

@ -42,6 +42,16 @@ const fetchSchemaList = () => (dispatch, getState) => {
schema: 'information_schema',
},
columns: ['schema_name'],
where: {
schema_name: {
$nin: [
'information_schema',
'pg_catalog',
'hdb_catalog',
'hdb_views',
],
},
},
},
}),
};

View File

@ -28,7 +28,7 @@ const DataHeader = ({
/*
const handleSchemaChange = e => {
const updatedSchema = e.target.value;
dispatch(push(globals.urlPrefix + '/data/schema/' + updatedSchema));
dispatch(push('/data/schema/' + updatedSchema));
Promise.all([
dispatch({ type: UPDATE_CURRENT_SCHEMA, currentSchema: updatedSchema }),
dispatch(loadSchema()),

View File

@ -129,7 +129,7 @@ const PageContainer = ({
styles.padd_left_remove
}
>
Tables
Tables ({schema.length})
</div>
{migrationMode ? (
<div

View File

@ -20,6 +20,12 @@
line-height: 26px;
}
.changeSchema {
height: 25px;
margin-left: 10px;
width: auto;
}
.sidebar {
height: calc(100vh - 26px);
overflow: auto;

View File

@ -4,7 +4,12 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { autoTrackRelations } from '../TableRelationships/Actions';
import {
autoTrackRelations,
autoAddRelName,
} from '../TableRelationships/Actions';
import { getRelationshipLine } from '../TableRelationships/Relationships';
import suggestedRelationshipsRaw from '../TableRelationships/autoRelations';
class AutoAddRelations extends Component {
componentWillMount() {
@ -14,20 +19,110 @@ class AutoAddRelations extends Component {
this.props.dispatch(autoTrackRelations());
};
render() {
// const styles = require('../PageContainer/PageContainer.scss');
const { schema, untrackedRelations, dispatch } = this.props;
const styles = require('../PageContainer/PageContainer.scss');
const handleAutoAddIndivRel = obj => {
dispatch(autoAddRelName(obj));
};
if (this.props.untrackedRelations.length === 0) {
return null;
if (untrackedRelations.length === 0) {
return (
<div
className={styles.display_inline + ' ' + styles.padd_bottom}
key="no-untracked-rel"
>
There are no untracked relations
</div>
);
}
const untrackedIndivHtml = [];
schema.map(table => {
const currentTable = table.table_name;
const currentTableRel = suggestedRelationshipsRaw(currentTable, schema);
currentTableRel.objectRel.map(obj => {
untrackedIndivHtml.push(
<div
className={styles.padd_top_medium}
key={'untrackedIndiv' + table.table_name}
>
<button
className={`${styles.display_inline} btn btn-xs btn-default`}
onClick={e => {
e.preventDefault();
handleAutoAddIndivRel(obj);
}}
>
Add
</button>
<div className={styles.display_inline + ' ' + styles.add_pad_left}>
<b>{obj.tableName}</b> -{' '}
{getRelationshipLine(
obj.isObjRel,
obj.lcol,
obj.rcol,
obj.rTable
)}
</div>
</div>
);
});
currentTableRel.arrayRel.map(obj => {
untrackedIndivHtml.push(
<div
className={styles.padd_top_medium}
key={'untrackedIndiv' + table.table_name}
>
<button
className={`${styles.display_inline} btn btn-xs btn-default`}
onClick={e => {
e.preventDefault();
handleAutoAddIndivRel(obj);
}}
>
Add
</button>
<div className={styles.display_inline + ' ' + styles.add_pad_left}>
<b>{obj.tableName}</b> -{' '}
{getRelationshipLine(
obj.isObjRel,
obj.lcol,
obj.rcol,
obj.rTable
)}
</div>
</div>
);
});
});
return (
<div>
{untrackedRelations.length === 0 ? (
<div
className={styles.display_inline + ' ' + styles.padd_bottom}
key="no-untracked-rel"
>
There are no untracked relations
</div>
) : (
<div
className={styles.display_inline + ' ' + styles.padd_bottom}
key="untracked-rel"
>
There are {untrackedRelations.length} untracked relations
</div>
)}
<button
onClick={this.trackAllRelations}
className={'btn btn-xs btn-default'}
className={
styles.display_inline +
' btn btn-xs btn-default ' +
styles.add_mar_left
}
data-test="track-all-relationships"
>
Track Available Relations
Track All Relations
</button>
<div className={styles.padd_top_small}>{untrackedIndivHtml}</div>
</div>
);
}
@ -35,6 +130,7 @@ class AutoAddRelations extends Component {
AutoAddRelations.propTypes = {
untrackedRelations: PropTypes.array.isRequired,
schema: PropTypes.array.isRequired,
dispatch: PropTypes.func.isRequired,
};

View File

@ -19,6 +19,7 @@ import {
loadUntrackedSchema,
fetchSchemaList,
LOAD_UNTRACKED_RELATIONS,
UPDATE_CURRENT_SCHEMA,
} from '../DataActions';
import { getAllUnTrackedRelations } from '../TableRelationships/Actions';
import AutoAddRelationsConnector from './AutoAddRelations';
@ -57,12 +58,24 @@ class Schema extends Component {
render() {
const {
schema,
schemaList,
untracked,
migrationMode,
untrackedRelations,
currentSchema,
dispatch,
} = this.props;
const handleSchemaChange = e => {
const updatedSchema = e.target.value;
dispatch(push('/data/schema/' + updatedSchema));
Promise.all([
dispatch({ type: UPDATE_CURRENT_SCHEMA, currentSchema: updatedSchema }),
dispatch(loadSchema()),
dispatch(loadUntrackedSchema()),
]);
};
const styles = require('../PageContainer/PageContainer.scss');
let relationships = 0;
@ -89,7 +102,6 @@ class Schema extends Component {
const untrackedHtml = [];
for (let i = 0; i < untrackedTables.length; i++) {
// if (untrackedTables[i].table_name !== 'schema_migrations') {
untrackedHtml.push(
<div className={styles.padd_bottom} key={`${i}untracked`}>
<div className={`${styles.display_inline} ${styles.padd_right}`}>
@ -110,7 +122,6 @@ class Schema extends Component {
</div>
</div>
);
// }
}
if (!untrackedHtml.length) {
untrackedHtml.push(
@ -147,18 +158,35 @@ class Schema extends Component {
) : null}
</div>
<hr />
<div className={styles.padd_bottom}>
There are <b>{schema.length}</b> tables tracked in the{' '}
{currentSchema} schema, with <b>{relationships}</b> relationships.
<div>
<div className={styles.display_inline}>Current postgres schema</div>
<div className={styles.display_inline}>
<select
onChange={handleSchemaChange}
className={styles.changeSchema + ' form-control'}
>
{schemaList.map(s => {
if (s.schema_name === currentSchema) {
return (
<option key={s.schema_name} selected="selected">
{s.schema_name}
</option>
);
}
return <option key={s.schema_name}>{s.schema_name}</option>;
})}
</select>
</div>
</div>
<div className={styles.padd_top}>
<hr />
<div className={styles.add_pad_bottom}>
<div>
<h4
className={`${styles.subheading_text} ${
styles.heading_tooltip
}`}
>
Untracked Tables/Views
Untracked tables or views
</h4>
<OverlayTrigger placement="right" overlay={untrackedTip}>
<i className="fa fa-info-circle" aria-hidden="true" />
@ -181,28 +209,27 @@ class Schema extends Component {
{untrackedHtml}
</div>
</div>
<div className={styles.padd_top}>
<div className={styles.padd_top}>
<hr />
<div>
<div>
<h4
className={`${styles.subheading_text} ${
styles.heading_tooltip
}`}
>
Untracked Relations
Untracked foreign-key relations
</h4>
<OverlayTrigger placement="right" overlay={untrackedRelTip}>
<i className="fa fa-info-circle" aria-hidden="true" />
</OverlayTrigger>
<div className={`${styles.padd_left_remove} col-xs-12`}>
{untrackedRelations.length === 0 ? (
<div key="no-untracked-rel">
There are no untracked relations
</div>
) : null}
<AutoAddRelationsConnector
untrackedRelations={untrackedRelations}
dispatch={dispatch}
/>
<div>
<AutoAddRelationsConnector
untrackedRelations={untrackedRelations}
schema={schema}
dispatch={dispatch}
/>
</div>
</div>
</div>
</div>
@ -223,6 +250,7 @@ Schema.propTypes = {
const mapStateToProps = state => ({
schema: state.tables.allSchemas,
schemaList: state.tables.schemaList,
untracked: state.tables.untrackedSchemas,
migrationMode: state.main.migrationMode,
untrackedRelations: state.tables.untrackedRelations,

View File

@ -10,15 +10,13 @@ export const dataAPI = (
export const untrackedTip = (
<Tooltip id="tooltip-data-service">
These are the tables/views in the schema which are not tracked by Data
Microservice
Tables or views that are not exposed over GraphQL
</Tooltip>
);
export const untrackedRelTip = (
<Tooltip id="tooltip-data-rel-service">
These are the relations in the schema which are not tracked by Data
Microservice
Foreign keys between tracked tables that are not relationships
</Tooltip>
);

View File

@ -144,6 +144,7 @@ const modifyReducer = (tableName, schemas, modifyStateOrig, action) => {
lcol: '',
rTable: null,
rcol: '',
manualColumns: [],
},
};
case REL_SET_TYPE:

View File

@ -1,4 +1,8 @@
import { makeMigrationCall, loadUntrackedRelations } from '../DataActions';
import {
makeMigrationCall,
loadUntrackedRelations,
loadSchema,
} from '../DataActions';
import gqlPattern, { gqlRelErrorNotif } from '../Common/GraphQLValidation';
import { showErrorNotification } from '../Notification';
import suggestedRelationshipsRaw from './autoRelations';
@ -410,6 +414,78 @@ const autoTrackRelations = () => (dispatch, getState) => {
);
};
const autoAddRelName = obj => (dispatch, getState) => {
const currentSchema = getState().tables.currentSchema;
const isObjRel = obj.isObjRel;
const relName = formRelName(obj);
const relChangesUp = [
{
type: isObjRel
? 'create_object_relationship'
: 'create_array_relationship',
args: {
name: relName,
table: { name: obj.tableName, schema: currentSchema },
using: isObjRel
? { foreign_key_constraint_on: obj.lcol }
: {
foreign_key_constraint_on: {
table: { name: obj.rTable, schema: currentSchema },
column: obj.rcol,
},
},
},
},
];
const relChangesDown = [
{
type: isObjRel
? 'create_object_relationship'
: 'create_array_relationship',
args: {
name: relName,
table: { name: obj.tableName, schema: currentSchema },
using: isObjRel
? { foreign_key_constraint_on: obj.lcol }
: {
foreign_key_constraint_on: {
table: { name: obj.rTable, schema: currentSchema },
column: obj.rcol,
},
},
},
},
];
// Apply migrations
const migrationName = `add_relationship_${relName}_table_${currentSchema}_${
obj.tableName
}`;
const requestMsg = 'Adding Relationship...';
const successMsg = 'Relationship created';
const errorMsg = 'Creating relationship failed';
const customOnSuccess = () => {
Promise.all([dispatch(loadSchema()), dispatch(loadUntrackedRelations())]);
};
const customOnError = () => {};
makeMigrationCall(
dispatch,
getState,
relChangesUp,
relChangesDown,
migrationName,
customOnSuccess,
customOnError,
requestMsg,
successMsg,
errorMsg
);
};
export {
deleteRelMigrate,
addNewRelClicked,
@ -424,6 +500,7 @@ export {
resetRelationshipForm,
relManualAddClicked,
autoTrackRelations,
autoAddRelName,
formRelName,
getAllUnTrackedRelations,
};

View File

@ -46,16 +46,13 @@ const getObjArrayRelationshipList = relationships => {
/* This function sets the styling to the way the relationship looks, for eg: id -> user::user_id */
const getRelationshipLine = (isObjRel, lcol, rcol, rTable) => {
const finalRTable = rTable.name ? rTable.name : rTable;
const getGrayText = value => <i>{value}</i>;
return isObjRel ? (
<span>
&nbsp;{getGrayText(lcol)}&nbsp;&nbsp;&rarr;&nbsp;&nbsp;{rTable} :: {rcol}
&nbsp;{lcol}&nbsp;&nbsp;&rarr;&nbsp;&nbsp;{rTable} :: {rcol}
</span>
) : (
<span>
&nbsp;{finalRTable} :: {rcol}&nbsp;&nbsp;&rarr;&nbsp;&nbsp;{getGrayText(
lcol
)}
&nbsp;{finalRTable} :: {rcol}&nbsp;&nbsp;&rarr;&nbsp;&nbsp;{lcol}
</span>
);
};
@ -777,4 +774,4 @@ const relationshipsConnector = connect =>
export default relationshipsConnector;
export { suggestedRelationships };
export { suggestedRelationships, getRelationshipLine };