console: mark inconsistent schemas in the UI (close #5093) (#5181)

This commit is contained in:
Sameer Kolhar 2020-08-24 17:01:45 +05:30 committed by GitHub
parent 41f35c684e
commit ad3cfb4681
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 208 additions and 188 deletions

View File

@ -20,6 +20,7 @@ This release contains the [PDV refactor (#4111)](https://github.com/hasura/graph
- server: miscellaneous description changes (#4111)
- server: treat the absence of `backend_only` configuration and `backend_only: false` equally (closing #5059) (#4111)
- console: allow user to cascade Postgres dependencies when dropping Postgres objects (close #5109) (#5248)
- console: mark inconsistent remote schemas in the UI (close #5093) (#5181)
- cli: add missing global flags for seeds command (#5565)
- docs: add docs page on networking with docker (close #4346) (#4811)

View File

@ -1,5 +1,5 @@
@import "~bootstrap-sass/assets/stylesheets/bootstrap/variables";
@import "../../Common.scss";
@import '~bootstrap-sass/assets/stylesheets/bootstrap/variables';
@import '../../Common.scss';
.container {
}
@ -28,8 +28,8 @@
// background: #444;
// color: $navbar-inverse-color;
color: #333;
border: 1px solid #E5E5E5;
background-color: #F8F8F8;
border: 1px solid #e5e5e5;
background-color: #f8f8f8;
/*
a,a:visited {
@ -66,7 +66,7 @@
*/
a {
color: #767E93;
color: #767e93;
word-wrap: break-word;
}
}
@ -75,9 +75,9 @@
padding: 7px 0;
// color: $navbar-inverse-link-hover-color;
transition: color 0.5s;
pointer: cursor;
cursor: pointer;
}
}
}
}
.main {
@ -113,7 +113,7 @@
.sidebarHeading {
font-weight: bold;
display: inline-block;
color: #767E93;
color: #767e93;
font-size: 15px;
}
}
@ -148,19 +148,23 @@
padding-left: 5px !important;
display: initial !important;
.tableIcon, .functionIcon {
//display: inline;
.tableIcon,
.functionIcon {
margin-right: 5px;
font-size: 12px;
width: 12px;
}
.icon_mar_left {
margin-left: 5px;
}
}
}
.noChildren {
font-weight: 400 !important;
padding-bottom: 10px !important;
color: #767E93 !important;
color: #767e93 !important;
}
li:first-child {
@ -170,11 +174,16 @@
.activeLink {
a {
// border-left: 4px solid #FFC627;
color: #FD9540!important;
color: #fd9540 !important;
}
}
.padLeft4 {
margin-left: 8px;
top: 12px;
font-size: 14px;
}
.floatRight {
float: right;
margin-right: 20px;

View File

@ -1,7 +1,4 @@
/* */
import { listState } from './state';
/* */
import Endpoints, { globalCookiePolicy } from '../../../Endpoints';
import requestAction from '../../../utils/requestAction';
import dataHeaders from '../Data/Common/Headers';
@ -10,9 +7,7 @@ import returnMigrateUrl from '../Data/Common/getMigrateUrl';
import { CLI_CONSOLE_MODE, SERVER_CONSOLE_MODE } from '../../../constants';
import { loadMigrationStatus } from '../../Main/Actions';
import { handleMigrationErrors } from '../../../utils/migration';
import { showSuccessNotification } from '../Common/Notification';
import { filterInconsistentMetadataObjects } from '../Settings/utils';
/* Action constants */
@ -51,20 +46,9 @@ const fetchRemoteSchemas = () => {
dispatch({ type: FETCH_REMOTE_SCHEMAS });
return dispatch(requestAction(url, options)).then(
data => {
let consistentRemoteSchemas = data;
const { inconsistentObjects } = getState().metadata;
if (inconsistentObjects.length > 0) {
consistentRemoteSchemas = filterInconsistentMetadataObjects(
data,
inconsistentObjects,
'remote_schemas'
);
}
dispatch({
type: REMOTE_SCHEMAS_FETCH_SUCCESS,
data: consistentRemoteSchemas,
data,
});
return Promise.resolve();
},

View File

@ -318,7 +318,6 @@ const modifyRemoteSchema = () => {
return (dispatch, getState) => {
const currState = getState().remoteSchemas.addData;
const remoteSchemaName = currState.name.trim().replace(/ +/g, '');
// const url = Endpoints.getSchema;
const upQueryArgs = [];
const downQueryArgs = [];
const migrationName = 'update_remote_schema_' + remoteSchemaName;
@ -345,9 +344,10 @@ const modifyRemoteSchema = () => {
},
};
resolveObj.definition.headers = [
...getReqHeader(getState().remoteSchemas.headerData.headers),
];
resolveObj.definition.headers = getReqHeader(
getState().remoteSchemas.headerData.headers
);
if (resolveObj.definition.url) {
delete resolveObj.definition.url_from_env;
} else {

View File

@ -22,6 +22,7 @@ import { NotFoundError } from '../../../Error/PageNotFound';
import globals from '../../../../Globals';
import { getConfirmation } from '../../../Common/utils/jsUtils';
import styles from '../RemoteSchema.scss';
const prefixUrl = globals.urlPrefix + appPrefix;
@ -49,17 +50,17 @@ class Edit extends React.Component {
]);
}
UNSAFE_componentWillReceiveProps(nextProps) {
componentDidUpdate(prevProps) {
if (
nextProps.params.remoteSchemaName !== this.props.params.remoteSchemaName
prevProps.params.remoteSchemaName !== this.props.params.remoteSchemaName
) {
Promise.all([
this.props.dispatch(
fetchRemoteSchema(nextProps.params.remoteSchemaName)
fetchRemoteSchema(this.props.params.remoteSchemaName)
),
this.props.dispatch({
type: VIEW_REMOTE_SCHEMA,
data: nextProps.params.remoteSchemaName,
data: this.props.params.remoteSchemaName,
}),
]);
}
@ -120,11 +121,23 @@ class Edit extends React.Component {
throw new NotFoundError();
}
const styles = require('../RemoteSchema.scss');
const { isFetching, isRequesting, editState } = this.props;
const {
isFetching,
isRequesting,
editState,
inconsistentObjects,
} = this.props;
const { remoteSchemaName } = this.props.params;
const inconsistencyDetails = inconsistentObjects.find(
inconObj =>
inconObj.type === 'remote_schema' &&
inconObj?.definition?.name === remoteSchemaName
);
const fixInconsistencyMsg =
'This remote schema is in an inconsistent state. Please fix inconsistencies and reload metadata first';
const generateMigrateBtns = () => {
return 'isModify' in editState && !editState.isModify ? (
<div className={styles.commonBtn}>
@ -137,7 +150,8 @@ class Edit extends React.Component {
this.modifyClick();
}}
data-test={'remote-schema-edit-modify-btn'}
disabled={isRequesting}
disabled={isRequesting || inconsistencyDetails}
title={inconsistencyDetails ? fixInconsistencyMsg : ''}
>
Modify
</Button>
@ -148,7 +162,8 @@ class Edit extends React.Component {
e.preventDefault();
this.handleDeleteRemoteSchema(e);
}}
disabled={isRequesting}
disabled={isRequesting || inconsistencyDetails}
title={inconsistencyDetails ? fixInconsistencyMsg : ''}
data-test={'remote-schema-edit-delete-btn'}
>
{isRequesting ? 'Deleting ...' : 'Delete'}
@ -254,6 +269,7 @@ const mapStateToProps = state => {
...state.remoteSchemas.headerData,
allRemoteSchemas: state.remoteSchemas.listData.remoteSchemas,
dataHeaders: { ...state.tables.dataHeaders },
inconsistentObjects: state.metadata.inconsistentObjects,
};
};

View File

@ -1,28 +1,56 @@
import React from 'react';
import CommonTabLayout from '../../../Common/Layout/CommonTabLayout/CommonTabLayout';
import tabInfo from './tabInfo';
import Tooltip from 'react-bootstrap/lib/Tooltip';
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
import { push } from 'react-router-redux';
import {
fetchRemoteSchema,
RESET,
getHeaderEvents,
} from '../Add/addRemoteSchemaReducer';
import { VIEW_REMOTE_SCHEMA } from '../Actions';
import ReloadRemoteSchema from '../../Settings/MetadataOptions/ReloadRemoteSchema';
import { appPrefix } from '../constants';
import { NotFoundError } from '../../../Error/PageNotFound';
import globals from '../../../../Globals';
import styles from '../RemoteSchema.scss';
import ToolTip from '../../../Common/Tooltip/Tooltip';
import WarningSymbol from '../../../Common/WarningSymbol/WarningSymbol';
const prefixUrl = globals.urlPrefix + appPrefix;
const RSHeadersDisplay = ({ data }) =>
data.length > 0 ? (
<tr>
<td>Headers</td>
<td>
{data &&
data
.filter(header => !!header.name)
.map((header, index) => [
<tr key={header}>
<td>
{`${header.name}: `}
{header.type === 'static'
? header.value
: '<' + header.value + '>'}
</td>
</tr>,
index !== data.length - 1 ? <hr /> : null,
])}
</td>
</tr>
) : null;
const RSReloadSchema = ({ readOnlyMode, remoteSchemaName, ...props }) =>
!readOnlyMode && remoteSchemaName && remoteSchemaName.length > 0 ? (
<div className={`${styles.commonBtn} ${styles.detailsRefreshButton}`}>
<ReloadRemoteSchema {...props} remoteSchemaName={remoteSchemaName} />
<ToolTip
placement="right"
message="If your remote schema has changed, you need to refresh the GraphQL Engine metadata to query the modified schema"
/>
</div>
) : null;
class ViewStitchedSchema extends React.Component {
componentDidMount() {
const { remoteSchemaName } = this.props.params;
@ -35,17 +63,17 @@ class ViewStitchedSchema extends React.Component {
]);
}
UNSAFE_componentWillReceiveProps(nextProps) {
componentDidUpdate(prevProps) {
if (
nextProps.params.remoteSchemaName !== this.props.params.remoteSchemaName
prevProps.params.remoteSchemaName !== this.props.params.remoteSchemaName
) {
Promise.all([
this.props.dispatch(
fetchRemoteSchema(nextProps.params.remoteSchemaName)
fetchRemoteSchema(this.props.params.remoteSchemaName)
),
this.props.dispatch({
type: VIEW_REMOTE_SCHEMA,
data: nextProps.params.remoteSchemaName,
data: this.props.params.remoteSchemaName,
}),
]);
}
@ -69,19 +97,14 @@ class ViewStitchedSchema extends React.Component {
}
render() {
const currentRemoteSchema = this.props.allRemoteSchemas.find(
r => r.name === this.props.params.remoteSchemaName
);
if (!currentRemoteSchema) {
// throw a 404 exception
throw new NotFoundError();
}
const styles = require('../RemoteSchema.scss');
const { remoteSchemaName } = this.props.params;
const { manualUrl, envName, headers, readOnlyMode } = this.props;
const {
manualUrl,
envName,
headers,
readOnlyMode,
inconsistentObjects,
} = this.props;
const filterHeaders = headers.filter(h => !!h.name);
@ -92,21 +115,14 @@ class ViewStitchedSchema extends React.Component {
},
{
title: 'Manage',
url: appPrefix + '/' + 'manage',
url: `${appPrefix}/manage`,
},
];
if (remoteSchemaName) {
breadCrumbs.push({
title: remoteSchemaName.trim(),
url:
appPrefix +
'/' +
'manage' +
'/' +
remoteSchemaName.trim() +
'/' +
'details',
url: `${appPrefix}/manage/${remoteSchemaName.trim()}/details`,
});
breadCrumbs.push({
title: 'details',
@ -114,33 +130,18 @@ class ViewStitchedSchema extends React.Component {
});
}
const refresh = (
<Tooltip id="tooltip-cascade">
If your remote schema has changed, you need to refresh the GraphQL
Engine metadata to query the modified schema
</Tooltip>
);
let tabInfoCopy = tabInfo;
if (readOnlyMode) {
delete tabInfo.modify;
const { modify, ...rest } = tabInfoCopy;
tabInfoCopy = rest;
}
const showReloadRemoteSchema =
!readOnlyMode && remoteSchemaName && remoteSchemaName.length > 0 ? (
<div className={styles.commonBtn + ' ' + styles.detailsRefreshButton}>
<span>
<ReloadRemoteSchema
{...this.props}
remoteSchemaName={remoteSchemaName}
/>
</span>
<span>
<OverlayTrigger placement="right" overlay={refresh}>
<i className="fa fa-question-circle" aria-hidden="true" />
</OverlayTrigger>
</span>
</div>
) : null;
const inconsistencyDetails = inconsistentObjects.find(
inconObj =>
inconObj.type === 'remote_schema' &&
inconObj?.definition?.name === remoteSchemaName
);
return (
<div
@ -150,7 +151,7 @@ class ViewStitchedSchema extends React.Component {
appPrefix={appPrefix}
currentTab="details"
heading={remoteSchemaName}
tabsInfo={tabInfo}
tabsInfo={tabInfoCopy}
breadCrumbs={breadCrumbs}
baseUrl={`${appPrefix}/manage/${remoteSchemaName}`}
/>
@ -164,37 +165,35 @@ class ViewStitchedSchema extends React.Component {
<td>GraphQL Server URL</td>
<td>{manualUrl || `<${envName}>`}</td>
</tr>
{filterHeaders.length > 0 ? (
<tr>
<td>Headers</td>
<td>
{filterHeaders &&
filterHeaders
.filter(k => !!k.name)
.map((h, i) => [
<tr key={i}>
<td>
{h.name} :{' '}
{h.type === 'static'
? h.value
: '<' + h.value + '>'}
</td>
</tr>,
i !== filterHeaders.length - 1 ? <hr /> : null,
])}
</td>
</tr>
) : null}
{/*
<tr>
<td>Webhook</td>
<td>in-use/bypassed</td>
</tr>
*/}
<RSHeadersDisplay data={filterHeaders} />
</tbody>
</table>
</div>
{showReloadRemoteSchema}
{inconsistencyDetails && (
<div className={styles.add_mar_bottom}>
<div className={styles.subheading_text}>
<WarningSymbol tooltipText={'Inconsistent schema'} />
<span className={styles.add_mar_left_mid}>
This remote schema is in an inconsistent state.
</span>
</div>
<div>
<b>Reason:</b> {inconsistencyDetails.reason}
</div>
<div>
<i>
(Please resolve the inconsistencies and reload the remote
schema. Fields from this remote schema are currently not
exposed over the GraphQL API)
</i>
</div>
</div>
)}
<RSReloadSchema
readOnlyMode={readOnlyMode}
remoteSchemaName={remoteSchemaName}
{...this.props}
/>
</div>
<br />
<br />
@ -210,6 +209,7 @@ const mapStateToProps = state => {
allRemoteSchemas: state.remoteSchemas.listData.remoteSchemas,
dataHeaders: { ...state.tables.dataHeaders },
readOnlyMode: state.main.readOnlyMode,
inconsistentObjects: state.metadata.inconsistentObjects,
};
};

View File

@ -1,4 +1,4 @@
@import "../../Common/Common.scss";
@import '../../Common/Common.scss';
.addPaddCommom {
padding: 10px 0;
@ -42,17 +42,12 @@
width: 100%;
padding: 20px;
text-align: center;
// height: 200px;
// border: 1px solid #000;
img {
}
}
.commonBtn {
text-align: center;
padding: 20px 0;
padding-bottom: 10px
padding-bottom: 10px;
}
.readMore {
@ -61,7 +56,7 @@
}
}
.iconWrapper{
.iconWrapper {
padding: 20px 0;
.icon {
@ -140,7 +135,7 @@
}
.red_button {
color: #FFF;
color: #fff;
}
a {
@ -166,7 +161,6 @@
.set_line_height {
line-height: 26px;
}
}
.remoteSchemaImg {
@ -193,7 +187,7 @@
display: flex;
align-items: center;
a{
a {
color: #909090;
}
@ -260,24 +254,24 @@
display: inline-block;
}
.instructionsWrapper, .instructionsWrapperPos {
.instructionsWrapper,
.instructionsWrapperPos {
margin-top: 20px;
border-top: 1px solid #DEDEDE;
border-top: 1px solid #dedede;
.instructions {
padding: 12px 0;
}
}
.instructionsWrapper
{
.instructionsWrapper {
position: relative;
}
.instructionsWrapperPos
{
.instructionsWrapperPos {
position: static;
}
.instructionsWrapper:hover, .instructionsWrapperPos:hover {
.instructionsWrapper:hover,
.instructionsWrapperPos:hover {
.instructions {
color: #505050
color: #505050;
}
.rightArrow {

View File

@ -5,10 +5,10 @@ import PropTypes from 'prop-types';
import LeftContainer from '../../Common/Layout/LeftContainer/LeftContainer';
import PageContainer from '../../Common/Layout/PageContainer/PageContainer';
import RemoteSchemaSubSidebar from './RemoteSchemaSubSidebar';
import styles from '../../Common/TableCommon/Table.scss';
class RemoteSchemaPageContainer extends React.Component {
render() {
const styles = require('../../Common/TableCommon/Table.scss');
const { appPrefix, children } = this.props;
const currentLocation = location.pathname;

View File

@ -2,6 +2,8 @@ import React from 'react';
import { Link } from 'react-router';
import LeftSubSidebar from '../../Common/Layout/LeftSubSidebar/LeftSubSidebar';
import styles from '../../Common/Layout/LeftSubSidebar/LeftSubSidebar.scss';
import WarningSymbol from '../../Common/WarningSymbol/WarningSymbol';
const RemoteSchemaSubSidebar = ({
appPrefix,
@ -12,8 +14,12 @@ const RemoteSchemaSubSidebar = ({
filterItem,
viewRemoteSchema,
main,
...props
}) => {
const styles = require('../../Common/Layout/LeftSubSidebar/LeftSubSidebar.scss');
const { inconsistentObjects } = props.metadata;
const inconsistentRemoteSchemas = inconsistentObjects.filter(
inconObject => inconObject.type === 'remote_schema'
);
function tableSearch(e) {
const searchTerm = e.target.value;
@ -35,7 +41,7 @@ const RemoteSchemaSubSidebar = ({
const getChildList = () => {
const _dataList = searchQuery ? filtered : dataList;
let childList;
let childList = [];
if (_dataList.length === 0) {
childList = (
<li
@ -46,34 +52,51 @@ const RemoteSchemaSubSidebar = ({
</li>
);
} else {
childList = _dataList.map((d, i) => {
let activeTableClass = '';
if (
d.name === viewRemoteSchema &&
location.pathname.includes(viewRemoteSchema)
) {
activeTableClass = styles.activeLink;
}
if (_dataList.length > 0) {
childList = _dataList.map((d, i) => {
let activeTableClass = '';
return (
<li
className={activeTableClass}
key={i}
data-test={`remote-schema-sidebar-links-${i + 1}`}
>
<Link
to={appPrefix + '/manage/' + d.name + '/details'}
data-test={d.name}
if (
d.name === viewRemoteSchema &&
location.pathname.includes(viewRemoteSchema)
) {
activeTableClass = styles.activeLink;
}
const inconsistentCurrentSchema = inconsistentRemoteSchemas.find(
elem => elem.definition.name === d.name
);
return (
<li
className={activeTableClass}
key={i}
data-test={`remote-schema-sidebar-links-${i + 1}`}
>
<i
className={styles.tableIcon + ' fa fa-code-fork'}
aria-hidden="true"
/>
{d.name}
</Link>
</li>
);
});
<Link
to={`${appPrefix}/manage/${d.name}/details`}
data-test={d.name}
>
<i
className={`${styles.tableIcon} fa fa-code-fork`}
aria-hidden="true"
/>
{d.name}
{inconsistentCurrentSchema ? (
<WarningSymbol
customStyle={styles.padLeft4}
tooltipText={
'This remote schema is in an inconsistent state. ' +
'Fields from this remote schema are currently not exposed over the GraphQL API'
}
tooltipPlacement="right"
/>
) : null}
</Link>
</li>
);
});
}
}
return childList;

View File

@ -208,11 +208,6 @@ const handleInconsistentObjects = inconsistentObjects => {
inconsistentObjects,
'functions'
);
const filteredRemoteSchemas = filterInconsistentMetadataObjects(
remoteSchemas,
inconsistentObjects,
'remote_schemas'
);
const filteredActions = filterInconsistentMetadataObjects(
actions,
inconsistentObjects,
@ -221,7 +216,7 @@ const handleInconsistentObjects = inconsistentObjects => {
dispatch(setConsistentSchema(filteredSchema));
dispatch(setConsistentFunctions(filteredFunctions));
dispatch(setConsistentRemoteSchemas(filteredRemoteSchemas));
dispatch(setConsistentRemoteSchemas(remoteSchemas));
dispatch(setActions(filteredActions));
}
};
@ -234,9 +229,7 @@ export const loadInconsistentObjects = (reloadConfig, successCb, failureCb) => {
const { shouldReloadMetadata, shouldReloadRemoteSchemas } = reloadConfig;
const loadQuery = shouldReloadMetadata
? getReloadCacheAndGetInconsistentObjectsQuery(
shouldReloadRemoteSchemas === false ? false : true
)
? getReloadCacheAndGetInconsistentObjectsQuery(shouldReloadRemoteSchemas)
: inconsistentObjectsQuery;
dispatch({ type: LOADING_METADATA });

View File

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Button from '../../../Common/Button/Button';
import { reloadRemoteSchema } from '../Actions';
import metaDataStyles from '../Settings.scss';
import {
showSuccessNotification,
@ -17,7 +18,6 @@ class ReloadRemoteSchema extends Component {
render() {
const { dispatch, remoteSchemaName } = this.props;
const { isReloading } = this.state;
const metaDataStyles = require('../Settings.scss');
const reloadRemoteMetadataHandler = () => {
this.setState({ isReloading: true });
dispatch(

View File

@ -133,7 +133,7 @@ const MetadataStatus = ({ dispatch, metadata }) => {
of the metadata
</div>
<div className={styles.add_mar_top_small}>
The console will also not be able to display these inconsistent
The console might also not be able to display these inconsistent
objects
</div>
</div>