update track relationship text in console (#1927)

This commit is contained in:
Rikin Kachhia
2019-04-03 15:07:14 +05:30
committed by GitHub
parent 29d6e85e42
commit 56c67509aa
10 changed files with 531 additions and 488 deletions

View File

@@ -879,15 +879,9 @@ code {
}
}
/*Newly added for remote schema page*/
.headerText {
font-size: 24px;
font-weight: bold;
padding-bottom: 20px;
}
.addPaddRight {
padding-right: 30px;
}
.subHeaderText {
@@ -912,13 +906,6 @@ code {
font-weight: 600;
}
.noPadd {
padding-left: 0;
padding-right: 0;
}
/*Newly added for remote schema page*/
.heading_text {
font-size: 18px;
font-weight: bold;

View File

@@ -16,7 +16,50 @@ class CustomResolver extends React.Component {
const { dispatch, migrationMode, customResolverList } = this.props;
const showFirstSection = customResolverList.resolvers.length ? false : true;
const getIntroSection = () => {
const showIntroSection = !customResolverList.resolvers.length;
if (!showIntroSection) {
return null;
}
return (
<div>
<TopicDescription
title="What are Remote Schemas?"
imgUrl="https://storage.googleapis.com/hasura-graphql-engine/console/assets/remote_schema.png"
imgAlt="Remote Schema"
description="Remote schemas are external GraphQL services which can be merged with Hasura to provide a unified GraphQL API. Think of it like automated schema stitching. All you need to do is build a GraphQL service and then provide its HTTP endpoint to Hasura. Your GraphQL service can be written in any language or framework."
/>
<hr className={styles.clear_fix} />
</div>
);
};
const getAddBtn = () => {
let addBtn = null;
if (migrationMode) {
const handleClick = e => {
e.preventDefault();
dispatch(push(`${globals.urlPrefix}${appPrefix}/manage/add`));
};
addBtn = (
<Button
data-test="data-create-remote-schemas"
color="yellow"
size="sm"
className={styles.add_mar_left}
onClick={handleClick}
>
Add
</Button>
);
}
return addBtn;
};
return (
<div
@@ -28,41 +71,14 @@ class CustomResolver extends React.Component {
<Helmet title={`${pageTitle}s | Hasura`} />
<div>
<div className={styles.display_flex}>
<h2
className={`${styles.headerText} ${styles.addPaddRight} ${
styles.inline_block
}`}
>
<h2 className={`${styles.headerText} ${styles.inline_block}`}>
Remote Schemas
</h2>
{migrationMode ? (
<Button
data-test="data-create-remote-schemas"
color="yellow"
size="sm"
onClick={e => {
e.preventDefault();
dispatch(
push(`${globals.urlPrefix}${appPrefix}/manage/add`)
);
}}
>
Add
</Button>
) : null}
{getAddBtn()}
</div>
<hr />
{showFirstSection ? (
<div>
<TopicDescription
title="What are Remote Schemas?"
imgUrl="https://storage.googleapis.com/hasura-graphql-engine/console/assets/remote_schema.png"
imgAlt="Remote Schema"
description="Remote schemas are external GraphQL services which can be merged with Hasura to provide a unified GraphQL API. Think of it like automated schema stitching. All you need to do is build a GraphQL service and then provide its HTTP endpoint to Hasura. Your GraphQL service can be written in any language or framework."
/>
<hr className={styles.clear_fix} />
</div>
) : null}
{getIntroSection()}
<TryItOut
service="remoteSchema"
@@ -76,34 +92,6 @@ class CustomResolver extends React.Component {
adMoreLink="https://github.com/hasura/graphql-engine/tree/master/community/boilerplates/remote-schemas/"
/>
</div>
{/*
<div className={styles.resolverContent}>
Add pre-CRUD custom business logic like data validation, etc. or also
fetch data from another GraphQL server by stitching schemas
</div>
<div className={styles.resolverImg}>
<img src={landingImage} />
</div>
<div className={styles.commonBtn}>
<Link
className={styles.padd_remove_full}
to={`${appPrefix}/manage/add`}
>
<button className={styles.yellow_button}>
Add Remote GraphQL schema
</button>
</Link>
</div>
<div className={styles.readMore}>
<a
href="https://docs.hasura.io/1.0/graphql/manual/schema/custom-logic/index.html"
target="_blank"
rel="noopener noreferrer"
>
Read more
</a>
</div>
*/}
</div>
</div>
);

View File

@@ -1,107 +0,0 @@
/* eslint-disable space-infix-ops */
/* eslint-disable no-loop-func */
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import {
autoTrackRelations,
autoAddRelName,
} from '../TableRelationships/Actions';
import { getRelationshipLine } from '../TableRelationships/Relationships';
import Button from '../../../Common/Button/Button';
class AutoAddRelations extends Component {
trackAllRelations = untrackedData => {
this.props.dispatch(autoTrackRelations(untrackedData));
};
render() {
const styles = require('../../../Common/Layout/LeftSubSidebar/LeftSubSidebar.scss');
const { untrackedRelations, dispatch } = this.props;
const handleAutoAddIndivRel = obj => {
dispatch(autoAddRelName(obj));
};
if (untrackedRelations.length === 0) {
return (
<div
className={styles.display_inline + ' ' + styles.padd_bottom}
key="no-untracked-rel"
>
There are no untracked relations
</div>
);
}
const untrackData = untrackedRelations.map((obj, i) => {
return (
<div
className={styles.padd_top_medium}
key={`${obj.data.tableName}-${obj.data.rTable}-${i}`}
>
<Button
className={`${styles.display_inline}`}
size="xs"
color="white"
onClick={e => {
e.preventDefault();
handleAutoAddIndivRel(obj);
}}
>
Add
</Button>
<div className={styles.display_inline + ' ' + styles.add_pad_left}>
<b>{obj.data.tableName}</b> -{' '}
{getRelationshipLine(
obj.data.isObjRel,
obj.data.lcol,
obj.data.rcol,
obj.data.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.bind(this, untrackedRelations)}
className={`${styles.display_inline} ${styles.add_mar_left}`}
color="white"
size="xs"
data-test="track-all-relationships"
>
Track All Relations
</Button>
<div className={styles.padd_top_small}>{untrackData}</div>
</div>
);
}
}
AutoAddRelations.propTypes = {
untrackedRelations: PropTypes.array.isRequired,
schema: PropTypes.array.isRequired,
dispatch: PropTypes.func.isRequired,
};
export default AutoAddRelations;

View File

@@ -28,26 +28,33 @@ import {
LOAD_UNTRACKED_RELATIONS,
UPDATE_CURRENT_SCHEMA,
} from '../DataActions';
import { getAllUnTrackedRelations } from '../TableRelationships/Actions';
import AutoAddRelationsConnector from './AutoAddRelations';
import {
autoAddRelName,
autoTrackRelations,
getAllUnTrackedRelations,
} from '../TableRelationships/Actions';
import globals from '../../../../Globals';
import { getRelDef } from '../TableRelationships/Relationships';
const appPrefix = globals.urlPrefix + '/data';
class Schema extends Component {
constructor(props) {
super(props);
this.state = {
isExporting: false,
};
// Initialize this table
const dispatch = this.props.dispatch;
dispatch(fetchDataInit());
dispatch(fetchFunctionInit());
this.props.dispatch(fetchDataInit());
this.props.dispatch(fetchFunctionInit());
const untrackedRelations = getAllUnTrackedRelations(
this.props.schema,
this.props.currentSchema
).bulkRelTrack;
this.props.dispatch({
type: LOAD_UNTRACKED_RELATIONS,
untrackedRelations,
@@ -59,6 +66,7 @@ class Schema extends Component {
this.props.schema,
this.props.currentSchema
).bulkRelTrack;
this.props.dispatch({
type: LOAD_UNTRACKED_RELATIONS,
untrackedRelations,
@@ -69,7 +77,7 @@ class Schema extends Component {
const {
schema,
schemaList,
untracked,
untrackedTables,
migrationMode,
untrackedRelations,
currentSchema,
@@ -81,20 +89,11 @@ class Schema extends Component {
const styles = require('../../../Common/Layout/LeftSubSidebar/LeftSubSidebar.scss');
/* Filter */
const trackedFuncs = trackedFunctions.map(t => t.function_name);
// Assuming schema for both function and tables are same
const trackableFuncs = functionsList.filter(f => {
// return function which are tracked && function name whose setof tables are tracked
return (
trackedFuncs.indexOf(f.function_name) === -1 && !!f.return_table_info
); // && add condition which will check whether the setoff table is tracked or not
});
/* */
const handleSchemaChange = e => {
const updatedSchema = e.target.value;
dispatch(push(`${appPrefix}/schema/${updatedSchema}`));
Promise.all([
dispatch({ type: UPDATE_CURRENT_SCHEMA, currentSchema: updatedSchema }),
dispatch(fetchDataInit()),
@@ -103,144 +102,271 @@ class Schema extends Component {
]);
};
let relationships = 0;
schema.map(t => (relationships += t.relationships.length));
/***********/
// find which tables are untracked
const ids1 = schema.map(item => item.table_name);
const ids2 = untracked.map(item => item.table_name);
const getTrackableFunctions = () => {
const trackedFuncNames = trackedFunctions.map(t => t.function_name);
const untrackedTables = ids1
.map((id, index) => {
if (ids2.indexOf(id) < 0) {
return schema[index];
}
})
.concat(
ids2.map((id, index) => {
if (ids1.indexOf(id) < 0) {
return untracked[index];
}
})
)
.filter(item => item !== undefined)
.sort((a, b) => {
// Assuming schema for both function and tables are same
// return function which are tracked && function name whose
// set of tables are tracked
const filterCondition = func => {
return (
!trackedFuncNames.includes(func.function_name) &&
!!func.return_table_info
);
};
return functionsList.filter(filterCondition);
};
const getUntrackedTables = () => {
const tableNames = schema.map(item => item.table_name);
const untrackedTableNames = untrackedTables.map(item => item.table_name);
const schemaUntrackedTables = schema.filter(
table => !untrackedTableNames.includes(table.table_name)
);
const untrackedTablesNotInSchema = untrackedTables.filter(
table => !tableNames.includes(table.table_name)
);
const tableSortFunc = (a, b) => {
return a.table_name === b.table_name
? 0
: +(a.table_name > b.table_name) || -1;
};
const _untrackedTables = schemaUntrackedTables.concat(
untrackedTablesNotInSchema
);
return _untrackedTables.sort(tableSortFunc);
};
/***********/
const allUntrackedTables = getUntrackedTables();
const trackableFuncs = getTrackableFunctions();
const getCreateBtn = () => {
let createBtn = null;
if (migrationMode) {
const handleClick = e => {
e.preventDefault();
dispatch(push(`${appPrefix}/schema/${currentSchema}/table/add`));
};
createBtn = (
<Button
data-test="data-create-table"
color="yellow"
size="sm"
className={styles.add_mar_left}
onClick={handleClick}
>
Create Table
</Button>
);
}
return createBtn;
};
const getCurrentSchemaSection = () => {
const schemaOptions = schemaList.map(s => {
return <option key={s.schema_name}>{s.schema_name}</option>;
});
const untrackedHtml = [];
for (let i = 0; i < untrackedTables.length; i++) {
untrackedHtml.push(
<div className={styles.padd_bottom} key={`${i}untracked`}>
<div className={`${styles.display_inline} ${styles.padd_right}`}>
return (
<div className={styles.add_mar_top}>
<div className={styles.display_inline}>Current Postgres schema</div>
<div className={styles.display_inline}>
<select
onChange={handleSchemaChange}
className={styles.changeSchema + ' form-control'}
value={currentSchema}
>
{schemaOptions}
</select>
</div>
</div>
);
};
const getUntrackedTablesSection = () => {
const getTrackAllBtn = () => {
let trackAllBtn = null;
if (allUntrackedTables.length > 0) {
trackAllBtn = (
<Button
data-test={`add-track-table-${untrackedTables[i].table_name}`}
className={`${styles.display_inline}`}
className={`${styles.display_inline} ${styles.addAllBtn}`}
color="white"
size="xs"
onClick={e => {
e.preventDefault();
dispatch(setTableName(untrackedTables[i].table_name));
dispatch(addExistingTableSql());
dispatch(addAllUntrackedTablesSql(allUntrackedTables));
}}
>
Add
Track All
</Button>
</div>
<div className={`${styles.padd_right} ${styles.inline_block}`}>
{untrackedTables[i].table_name}
</div>
</div>
);
}
if (!untrackedHtml.length) {
untrackedHtml.push(
<div key="no-untracked">There are no untracked tables or views</div>
);
}
);
}
return (
<div
className={`${styles.padd_left_remove} container-fluid ${
styles.padd_top
}`}
>
<div className={styles.padd_left}>
<Helmet title="Schema - Data | Hasura" />
<div>
<h2 className={`${styles.heading_text} ${styles.inline_block}`}>
{' '}
Schema{' '}
</h2>
{migrationMode ? (
<Button
data-test="data-create-table"
color="yellow"
size="sm"
onClick={e => {
e.preventDefault();
dispatch(
push(`${appPrefix}/schema/${currentSchema}/table/add`)
);
}}
>
Create Table
</Button>
) : null}
</div>
<hr />
<div>
<div className={styles.display_inline}>Current postgres schema</div>
<div className={styles.display_inline}>
<select
onChange={handleSchemaChange}
className={styles.changeSchema + ' form-control'}
value={currentSchema}
>
{schemaList.map(s => {
if (s.schema_name === currentSchema) {
return <option key={s.schema_name}>{s.schema_name}</option>;
}
return <option key={s.schema_name}>{s.schema_name}</option>;
})}
</select>
</div>
</div>
<hr />
<div className={styles.add_pad_bottom}>
<div>
<h4
className={`${styles.subheading_text} ${
styles.heading_tooltip
}`}
>
Untracked tables or views
</h4>
<OverlayTrigger placement="right" overlay={untrackedTip}>
<i className="fa fa-info-circle" aria-hidden="true" />
</OverlayTrigger>
{untrackedTables.length > 0 ? (
return trackAllBtn;
};
const getUntrackedTablesList = () => {
const untrackedTablesList = [];
allUntrackedTables.forEach((table, i) => {
const handleTrackTable = e => {
e.preventDefault();
dispatch(setTableName(table.table_name));
dispatch(addExistingTableSql());
};
untrackedTablesList.push(
<div className={styles.padd_bottom} key={`untracked-${i}`}>
<div className={styles.inline_block}>
<Button
className={`${styles.display_inline} ${styles.addAllBtn}`}
data-test={`add-track-table-${table.table_name}`}
className={`${styles.display_inline}`}
color="white"
size="xs"
onClick={e => {
e.preventDefault();
dispatch(addAllUntrackedTablesSql(untrackedTables));
}}
onClick={handleTrackTable}
>
Add all
Track
</Button>
) : null}
</div>
<div className={`${styles.padd_left_remove} col-xs-12`}>
{untrackedHtml}
</div>
<div className={styles.inline_block}>{table.table_name}</div>
</div>
);
});
if (!untrackedTablesList.length) {
untrackedTablesList.push(
<div key="no-untracked">There are no untracked tables or views</div>
);
}
return untrackedTablesList;
};
return (
<div className={styles.add_mar_top}>
<div>
<h4
className={`${styles.subheading_text} ${styles.heading_tooltip}`}
>
Untracked tables or views
</h4>
<OverlayTrigger placement="right" overlay={untrackedTip}>
<i className="fa fa-info-circle" aria-hidden="true" />
</OverlayTrigger>
{getTrackAllBtn()}
</div>
<hr />
<div className={styles.wd100 + ' ' + styles.clear_fix}>
<div className={`${styles.padd_left_remove} col-xs-12`}>
{getUntrackedTablesList()}
</div>
<div className={styles.clear_fix} />
</div>
);
};
const getUntrackedRelationsSection = () => {
const getTrackAllBtn = () => {
let trackAllBtn = null;
const trackAllRelations = () => {
this.props.dispatch(autoTrackRelations(untrackedRelations));
};
if (untrackedRelations.length > 0) {
trackAllBtn = (
<Button
onClick={trackAllRelations}
className={`${styles.display_inline} ${styles.add_mar_left}`}
color="white"
size="xs"
data-test="track-all-relationships"
>
Track All
</Button>
);
}
return trackAllBtn;
};
const getUntrackedRelList = () => {
const untrackedRelList = [];
untrackedRelations.forEach((rel, i) => {
const relData = rel.data;
const handleAddRel = e => {
e.preventDefault();
dispatch(autoAddRelName(rel));
};
const relFrom = <b>{relData.tableName}</b>;
const relTo = relData.isObjRel ? (
<b>{relData.rTable}</b>
) : (
<b>[ {relData.rTable} ]</b>
);
untrackedRelList.push(
<div className={styles.padd_bottom} key={`untracked-rel-${i}`}>
<div className={styles.inline_block}>
<Button
className={styles.display_inline}
color="white"
size="xs"
onClick={handleAddRel}
>
Track
</Button>
</div>
<div className={styles.inline_block}>
<span>
{relFrom} &rarr; {relTo}
</span>
&nbsp;&nbsp; - &nbsp;&nbsp;
<span>
{getRelDef(
relData.isObjRel,
relData.lcol,
relData.rcol,
relData.tableName,
relData.rTable
)}
</span>
</div>
</div>
);
});
if (!untrackedRelList.length) {
untrackedRelList.push(
<div key="no-untracked-rel">There are no untracked relations</div>
);
}
return untrackedRelList;
};
return (
<div className={styles.add_mar_top}>
<div>
<h4
className={`${styles.subheading_text} ${styles.heading_tooltip}`}
>
@@ -249,120 +375,139 @@ class Schema extends Component {
<OverlayTrigger placement="right" overlay={untrackedRelTip}>
<i className="fa fa-info-circle" aria-hidden="true" />
</OverlayTrigger>
<div className={`${styles.padd_left_remove} col-xs-12`}>
<div>
<AutoAddRelationsConnector
untrackedRelations={untrackedRelations}
schema={schema}
dispatch={dispatch}
/>
</div>
</div>
{getTrackAllBtn()}
</div>
<div className={`${styles.padd_left_remove} col-xs-12`}>
{getUntrackedRelList()}
</div>
<div className={styles.clear_fix} />
</div>
);
};
{trackableFuncs.length > 0
? [
<hr
className={styles.wd100 + ' ' + styles.clear_fix}
key={'custom-functions-hr'}
/>,
<div
className={styles.wd100 + ' ' + styles.clear_fix}
key={'custom-functions-content'}
>
<h4
className={`${styles.subheading_text} ${
styles.heading_tooltip
}`}
>
Untracked custom functions
</h4>
<OverlayTrigger
placement="right"
overlay={trackableFunctions}
>
<i className="fa fa-info-circle" aria-hidden="true" />
</OverlayTrigger>
<div className={`${styles.padd_left_remove} col-xs-12`}>
{trackableFuncs.map((p, i) => (
<div
className={styles.padd_bottom}
key={`${i}untracked-function`}
>
<div
className={`${styles.display_inline} ${
styles.padd_right
}`}
>
<Button
data-test={`add-track-function-${p.function_name}`}
className={`${
styles.display_inline
} btn btn-xs btn-default`}
onClick={e => {
e.preventDefault();
dispatch(addExistingFunction(p.function_name));
}}
>
Add
</Button>
</div>
<div
className={`${styles.padd_right} ${
styles.inline_block
}`}
>
{p.function_name}
</div>
</div>
))}
</div>
</div>,
]
: null}
const getUntrackedFunctionsSection = () => {
let trackableFunctionList = null;
{/* nonTrackableFunctionsList.length > 0
? [
<hr
className={styles.wd100 + ' ' + styles.clear_fix}
key={'non-trackable-custom-functions-id'}
/>,
<div
className={styles.wd100 + ' ' + styles.clear_fix}
key={'non-trackable-custom-functions-content'}
if (trackableFuncs.length > 0) {
trackableFunctionList = (
<div className={styles.add_mar_top} key={'custom-functions-content'}>
<div>
<h4
className={`${styles.subheading_text} ${
styles.heading_tooltip
}`}
>
<h4
className={`${styles.subheading_text} ${
styles.heading_tooltip
}`}
Untracked custom functions
</h4>
<OverlayTrigger placement="right" overlay={trackableFunctions}>
<i className="fa fa-info-circle" aria-hidden="true" />
</OverlayTrigger>
</div>
<div className={`${styles.padd_left_remove} col-xs-12`}>
{trackableFuncs.map((p, i) => (
<div
className={styles.padd_bottom}
key={`${i}untracked-function`}
>
Non trackable custom functions
</h4>
<OverlayTrigger
placement="right"
overlay={nonTrackableFunctions}
>
<i className="fa fa-info-circle" aria-hidden="true" />
</OverlayTrigger>
<div className={`${styles.padd_left_remove} col-xs-12`}>
{nonTrackableFunctionsList.map((p, i) => (
<div
className={styles.padd_bottom}
key={`${i}untracked-function`}
<div
className={`${styles.display_inline} ${styles.padd_right}`}
>
<Button
data-test={`add-track-function-${p.function_name}`}
className={`${
styles.display_inline
} btn btn-xs btn-default`}
onClick={e => {
e.preventDefault();
dispatch(addExistingFunction(p.function_name));
}}
>
<div
className={`${styles.padd_right} ${
styles.inline_block
}`}
>
{p.function_name}
</div>
</div>
))}
Track
</Button>
</div>
<div
className={`${styles.padd_right} ${styles.inline_block}`}
>
{p.function_name}
</div>
</div>
</div>,
]
: null */}
))}
</div>
<div className={styles.clear_fix} />
</div>
);
}
return trackableFunctionList;
};
const getNonTrackableFunctionsSection = () => {
const nonTrackableFuncList = null;
// if (nonTrackableFunctionsList.length > 0) {
// nonTrackableFuncList = (
// <div
// className={styles.add_mar_top}
// key={'non-trackable-custom-functions-content'}
// >
// <div>
// <h4
// className={`${styles.subheading_text} ${
// styles.heading_tooltip
// }`}
// >
// Non trackable custom functions
// </h4>
// <OverlayTrigger
// placement="right"
// overlay={nonTrackableFunctions}
// >
// <i className="fa fa-info-circle" aria-hidden="true" />
// </OverlayTrigger>
// </div>
// <div className={`${styles.padd_left_remove} col-xs-12`}>
// {nonTrackableFunctionsList.map((p, i) => (
// <div
// className={styles.padd_bottom}
// key={`${i}untracked-function`}
// >
// <div
// className={`${styles.padd_right} ${
// styles.inline_block
// }`}
// >
// {p.function_name}
// </div>
// </div>
// ))}
// </div>
// <div className={styles.clear_fix} />
// </div>
// );
// }
return nonTrackableFuncList;
};
return (
<div
className={`container-fluid ${styles.padd_left_remove} ${
styles.padd_top
}`}
>
<div className={styles.padd_left}>
<Helmet title="Schema - Data | Hasura" />
<div className={styles.display_flex}>
<h2 className={`${styles.headerText} ${styles.inline_block}`}>
Schema
</h2>
{getCreateBtn()}
</div>
{getCurrentSchemaSection()}
{getUntrackedTablesSection()}
{getUntrackedRelationsSection()}
{getUntrackedFunctionsSection()}
{getNonTrackableFunctionsSection()}
</div>
</div>
);
@@ -371,7 +516,7 @@ class Schema extends Component {
Schema.propTypes = {
schema: PropTypes.array.isRequired,
untracked: PropTypes.array.isRequired,
untrackedTables: PropTypes.array.isRequired,
untrackedRelations: PropTypes.array.isRequired,
migrationMode: PropTypes.bool.isRequired,
currentSchema: PropTypes.string.isRequired,
@@ -381,7 +526,7 @@ Schema.propTypes = {
const mapStateToProps = state => ({
schema: state.tables.allSchemas,
schemaList: state.tables.schemaList,
untracked: state.tables.untrackedSchemas,
untrackedTables: state.tables.untrackedSchemas,
migrationMode: state.main.migrationMode,
untrackedRelations: state.tables.untrackedRelations,
currentSchema: state.tables.currentSchema,

View File

@@ -1,6 +1,6 @@
/* eslint-disable jsx-a11y/no-autofocus */
import React from 'react';
import { getRelationshipLine } from './utils';
import { getRelDef } from './utils';
import Button from '../../../Common/Button/Button';
import { deleteRelMigrate, saveRenameRelationship } from './Actions';
import { showErrorNotification } from '../Notification';
@@ -66,11 +66,14 @@ class RelationshipEditor extends React.Component {
relName,
relConfig,
isObjRel,
tableStyles,
allowRename,
} = this.props;
const { text, isEditting } = this.state;
const { lcol, rtable, rcol } = relConfig;
const tableStyles = require('../../../Common/TableCommon/TableStyles.scss');
const onDelete = e => {
e.preventDefault();
const isOk = confirm('Are you sure?');
@@ -97,7 +100,7 @@ class RelationshipEditor extends React.Component {
&nbsp;
<b>{relName}</b>
<div className={tableStyles.relationshipTopPadding}>
{getRelationshipLine(isObjRel, lcol, rcol, rtable)}
{getRelDef(isObjRel, lcol, rcol, tableName, rtable)}
</div>
</div>
);
@@ -115,7 +118,7 @@ class RelationshipEditor extends React.Component {
</Button>
</div>
<div className={tableStyles.relationshipTopPadding}>
<div>{getRelationshipLine(isObjRel, lcol, rcol, rtable)}</div>
<div>{getRelDef(isObjRel, lcol, rcol, tableName, rtable)}</div>
<input
onChange={this.handleTextChange}
className={`form-control ${styles.add_mar_top_small}`}

View File

@@ -16,7 +16,7 @@ import { findAllFromRel } from '../utils';
import { showErrorNotification } from '../Notification';
import { setTable } from '../DataActions';
import gqlPattern, { gqlRelErrorNotif } from '../Common/GraphQLValidation';
import { getRelationshipLine } from './utils';
import { getRelDef } from './utils';
import AddManualRelationship from './AddManualRelationship';
import suggestedRelationshipsRaw from './autoRelations';
@@ -51,10 +51,11 @@ const addRelationshipCellView = (
rel,
selectedRelationship,
selectedRelationshipName,
tableStyles,
relMetaData,
tableSchema
) => {
const tableStyles = require('../../../Common/TableCommon/TableStyles.scss');
const onAdd = e => {
e.preventDefault();
dispatch(relSelectionChanged(rel));
@@ -109,7 +110,14 @@ const addRelationshipCellView = (
Add
</Button>
)}
{getRelationshipLine(rel.isObjRel, rel.lcol, rel.rcol, rel.rTable)}{' '}
&nbsp;
{getRelDef(
rel.isObjRel,
rel.lcol,
rel.rcol,
tableSchema.table_name,
rel.rTable
)}{' '}
&nbsp;
</div>
{selectedRelationship === rel ? (
@@ -148,15 +156,17 @@ const AddRelationship = ({
allSchemas,
cachedRelationshipData,
dispatch,
tableStyles,
}) => {
const styles = require('../TableModify/ModifyTable.scss');
const tableStyles = require('../../../Common/TableCommon/TableStyles.scss');
const cTable = allSchemas.find(t => t.table_name === tableName);
const suggestedRelationshipsData = suggestedRelationshipsRaw(
tableName,
allSchemas
);
const styles = require('../TableModify/ModifyTable.scss');
if (
suggestedRelationshipsData.objectRel.length < 1 &&
suggestedRelationshipsData.arrayRel.length < 1
@@ -231,7 +241,6 @@ const AddRelationship = ({
rel,
selectedRelationship,
relName,
tableStyles,
['object', i],
cTable
)
@@ -251,7 +260,6 @@ const AddRelationship = ({
rel,
selectedRelationship,
relName,
tableStyles,
['array', i],
cTable
)
@@ -429,7 +437,6 @@ class Relationships extends Component {
rel.objRel
)}
isObjRel
tableStyles={tableStyles}
allowRename={this.state.supportRename}
/>
) : (
@@ -447,7 +454,6 @@ class Relationships extends Component {
rel.arrRel
)}
isObjRel={false}
tableStyles={tableStyles}
allowRename={this.state.supportRename}
/>
) : (
@@ -488,7 +494,6 @@ class Relationships extends Component {
tableName={tableName}
allSchemas={allSchemas}
cachedRelationshipData={relAdd}
tableStyles={tableStyles}
dispatch={dispatch}
/>
</div>
@@ -581,4 +586,4 @@ const relationshipsConnector = connect =>
export default relationshipsConnector;
export { getRelationshipLine };
export { getRelDef };

View File

@@ -133,7 +133,7 @@ class RelationshipsView extends Component {
</thead>
<tbody>
{getObjArrayRelationshipList(tableSchema.relationships).map(
(rel, i) => {
rel => {
const column1 = rel.objRel ? (
<RelationshipEditor
dispatch={dispatch}
@@ -146,7 +146,6 @@ class RelationshipsView extends Component {
rel.objRel
)}
isObjRel
tableStyles={tableStyles}
allowRename={this.state.supportRename}
/>
) : (
@@ -164,7 +163,6 @@ class RelationshipsView extends Component {
rel.arrRel
)}
isObjRel={false}
tableStyles={tableStyles}
allowRename={this.state.supportRename}
/>
) : (

View File

@@ -1,21 +1,20 @@
import React from 'react';
/* This function sets the styling to the way the relationship looks, for eg: id -> user::user_id */
export const getRelationshipLine = (isObjRel, lcol, rcol, rTable) => {
const finalRTable = rTable.name ? rTable.name : rTable;
/* This function sets the styling to the way the relationship looks, for eg: article.id -> user.user_id */
export const getRelDef = (isObjRel, lcol, rcol, lTable, _rTable) => {
const rTable = _rTable.name ? _rTable.name : _rTable;
return isObjRel ? (
<span>
&nbsp;
{lcol.join(',')}
&nbsp;&nbsp;&rarr;&nbsp;&nbsp;
{rTable} :: {rcol.join(',')}
{lTable} . {lcol.join(',')}
&nbsp;&rarr;&nbsp;
{_rTable} . {rcol.join(',')}
</span>
) : (
<span>
&nbsp;
{finalRTable} :: {rcol.join(',')}
&nbsp;&nbsp;&rarr;&nbsp;&nbsp;
{lcol.join(',')}
{rTable} . {rcol.join(',')}
&nbsp;&rarr;&nbsp;
{lTable} . {lcol.join(',')}
</span>
);
};

View File

@@ -27,18 +27,64 @@ class Schema extends Component {
const styles = require('../../../Common/Layout/LeftSubSidebar/LeftSubSidebar.scss');
const showFirstSection = listingTrigger.length ? false : true;
const queryDefinition = `mutation {
insert_user(objects: [{name: "testuser"}] ){
affected_rows
}
}`;
const getIntroSection = () => {
const showIntroSection = !listingTrigger.length;
if (!showIntroSection) {
return null;
}
return (
<div>
<TopicDescription
title="What are Event Triggers?"
imgUrl="https://storage.googleapis.com/hasura-graphql-engine/console/assets/event-trigger.png"
imgAlt="Event Triggers"
description="Hasura can be used to create event triggers on tables. An Event Trigger atomically captures events (insert, update, delete) on a specified table and then reliably calls a webhook that can carry out any custom logic."
/>
<hr className={styles.clear_fix} />
</div>
);
};
const getAddBtn = () => {
let addBtn = null;
if (migrationMode) {
const handleClick = e => {
e.preventDefault();
dispatch(push(`${appPrefix}/manage/triggers/add`));
};
addBtn = (
<Button
data-test="data-create-trigger"
color="yellow"
size="sm"
className={styles.add_mar_left}
onClick={handleClick}
>
Add
</Button>
);
}
return addBtn;
};
const footerEvent = (
<span>
Head to the Events tab and see an event invoked under{' '}
<span className={styles.fontWeightBold}> test-trigger</span>.
</span>
);
return (
<div
className={`${styles.padd_left_remove} container-fluid ${
@@ -48,39 +94,15 @@ class Schema extends Component {
<div className={styles.padd_left}>
<Helmet title="Event Triggers | Hasura" />
<div className={styles.display_flex}>
<h2
className={`${styles.headerText} ${styles.addPaddRight} ${
styles.inline_block
}`}
>
Event Triggers{' '}
<h2 className={`${styles.headerText} ${styles.inline_block}`}>
Event Triggers
</h2>
{migrationMode ? (
<Button
data-test="data-create-trigger"
color="yellow"
size="sm"
onClick={e => {
e.preventDefault();
dispatch(push(`${appPrefix}/manage/triggers/add`));
}}
>
Add
</Button>
) : null}
{getAddBtn()}
</div>
<hr />
{showFirstSection ? (
<div>
<TopicDescription
title="What are Event Triggers?"
imgUrl="https://storage.googleapis.com/hasura-graphql-engine/console/assets/event-trigger.png"
imgAlt="Event Triggers"
description="Hasura can be used to create event triggers on tables. An Event Trigger atomically captures events (insert, update, delete) on a specified table and then reliably calls a webhook that can carry out any custom logic."
/>
<hr className={styles.clear_fix} />
</div>
) : null}
{getIntroSection()}
<TryItOut
service="eventTrigger"
title="Steps to deploy an example Event Trigger to Glitch"

View File

@@ -21,13 +21,13 @@ To track a table or a view:
^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. Head to the ``Data -> Schema`` section of the console.
#. Under the heading ``Untracked Tables/Views``, click on the ``Add`` button next to the table/view name.
#. Under the heading ``Untracked Tables/Views``, click on the ``Track`` button next to the table/view name.
To track all tables and views present in the database:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. Head to the ``Data -> Schema`` section of the console.
#. Under the heading ``Untracked Tables/Views``, click the ``Add all`` button.
#. Under the heading ``Untracked Tables/Views``, click the ``Track All`` button.
Step 2: Track foreign-keys
--------------------------
@@ -50,7 +50,7 @@ To track all the foreign-keys of all tables in the database:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. Head to the ``Data -> Schema`` section of the console.
#. Under the heading ``Untracked foreign-key relations``, click on the ``Track All Relations`` to automatically
#. Under the heading ``Untracked foreign-key relations``, click the ``Track All`` button to automatically
create relationships based on the foreign-keys.
.. admonition:: Relationship nomenclature
@@ -61,11 +61,14 @@ To track all the foreign-keys of all tables in the database:
The name is generated in the following format:
- For object relationships: ``Camel case of (foreignTableName + By + columnName)``
- For array relationships: ``Camel case of (foreignTableName + s + By + columnNameInForeignTable)``
- For object relationships: ``singular of foreignTableName``
- For array relationships: ``plural of foreignTableName``
For example, for the foreign-key ``article::author_id -> author::id``, the relationship names will be
``authorByAuthorId`` for ``article`` table and ``articlesByAuthorId`` for ``author`` table.
For example, for the foreign-key ``article.author_id -> author.id``, the relationship names will be
``author`` for ``article`` table and ``articles`` for ``author`` table.
In case a field with the generated name already exists, a new name will be generated of the form:
``camel case of (singular/plural of foreignTableName + _by_ + foreignKeyColumnName)``
Note that, **this is just an arbitrary naming convention** chosen by Hasura to ensure generation of unique
relationship names. You can choose to rename your relationships to anything you wish. You can **change the