mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-11-10 10:29:12 +03:00
Merge branch 'master' into remote-relationship-validation-bug-5133
This commit is contained in:
commit
95b08086a8
13
.circleci/server-upgrade-downgrade/err_msg.patch
Normal file
13
.circleci/server-upgrade-downgrade/err_msg.patch
Normal file
@ -0,0 +1,13 @@
|
||||
diff --git a/server/tests-py/validate.py b/server/tests-py/validate.py
|
||||
index 3eecd52a..a18b3113 100644
|
||||
--- a/server/tests-py/validate.py
|
||||
+++ b/server/tests-py/validate.py
|
||||
@@ -318,7 +318,7 @@ def assert_graphql_resp_expected(resp_orig, exp_response_orig, query, resp_hdrs=
|
||||
# If it is a batch GraphQL query, compare each individual response separately
|
||||
for (exp, out) in zip(as_list(exp_response), as_list(resp)):
|
||||
matched_ = equal_CommentedMap(exp, out)
|
||||
- if is_err_msg(exp):
|
||||
+ if is_err_msg(exp) and is_err_msg(out):
|
||||
if not matched_:
|
||||
warnings.warn("Response does not have the expected error message\n" + dump_str.getvalue())
|
||||
return resp, matched
|
@ -6,7 +6,9 @@
|
||||
# and sets some of the required variables that run.sh needs,
|
||||
# before executing run.sh
|
||||
set -euo pipefail
|
||||
ROOT="${BASH_SOURCE[0]%/*}"
|
||||
cd "${BASH_SOURCE[0]%/*}"
|
||||
ROOT="${PWD}"
|
||||
cd - > /dev/null
|
||||
|
||||
SERVER_DIR="$ROOT/../../server"
|
||||
|
||||
@ -18,8 +20,8 @@ echo "server binary: $SERVER_BINARY"
|
||||
cd -
|
||||
set +x
|
||||
|
||||
export SERVER_OUTPUT_DIR="server-output"
|
||||
export LATEST_SERVER_BINARY="./graphql-engine-latest"
|
||||
export SERVER_OUTPUT_DIR="$ROOT/server-output"
|
||||
export LATEST_SERVER_BINARY="$ROOT/graphql-engine-latest"
|
||||
|
||||
# Create Python virtualenv
|
||||
if ! [ -f ".venv/bin/activate" ] ; then
|
||||
@ -40,7 +42,8 @@ log_duration=on
|
||||
port=$PG_PORT
|
||||
EOF
|
||||
)
|
||||
export HASURA_GRAPHQL_DATABASE_URL="postgres://postgres:$PGPASSWORD@127.0.0.1:$PG_PORT/postgres"
|
||||
# Pytest is giving out deprecated warnings when postgres:// is used
|
||||
export HASURA_GRAPHQL_DATABASE_URL="postgresql://postgres:$PGPASSWORD@127.0.0.1:$PG_PORT/postgres"
|
||||
|
||||
DOCKER_PSQL="docker exec -u postgres -it $PG_CONTAINER_NAME psql -p $PG_PORT"
|
||||
|
||||
|
@ -12,7 +12,9 @@ set -euo pipefail
|
||||
# # echo an error message before exiting
|
||||
# trap 'echo "\"${last_command}\" command filed with exit code $?."' EXIT
|
||||
|
||||
ROOT="${BASH_SOURCE[0]%/*}"
|
||||
cd "${BASH_SOURCE[0]%/*}"
|
||||
ROOT="${PWD}"
|
||||
cd - > /dev/null
|
||||
|
||||
download_with_etag_check() {
|
||||
URL="$1"
|
||||
@ -119,6 +121,17 @@ trap rm_worktree ERR
|
||||
|
||||
make_latest_release_worktree() {
|
||||
git worktree add --detach "$WORKTREE_DIR" "$RELEASE_VERSION"
|
||||
cd "$WORKTREE_DIR"
|
||||
# FIX ME: Remove the patch below after the next stable release
|
||||
# The --avoid-error-message-checks in pytest was implementated as a rather relaxed check than
|
||||
# what we intended to have. In versions <= v1.3.0,
|
||||
# this check allows response to be success even if the expected response is a failure.
|
||||
# The patch below fixes that issue.
|
||||
# The `git apply` should give errors from next release onwards,
|
||||
# since this change is going to be included in the next release version
|
||||
git apply "${ROOT}/err_msg.patch" || \
|
||||
(log "Remove the git apply in make_latest_release_worktree function" && false)
|
||||
cd - > /dev/null
|
||||
}
|
||||
|
||||
cleanup_hasura_metadata_if_present() {
|
||||
@ -148,7 +161,18 @@ get_server_upgrade_tests() {
|
||||
cd $RELEASE_PYTEST_DIR
|
||||
tmpfile="$(mktemp --dry-run)"
|
||||
set -x
|
||||
python3 -m pytest -q --collect-only --collect-upgrade-tests-to-file "$tmpfile" -m 'allow_server_upgrade_test and not skip_server_upgrade_test' "${args[@]}" 1>/dev/null 2>/dev/null
|
||||
# FIX ME: Deselecting some introspection tests from the previous test suite
|
||||
# which throw errors on the latest build. Even when the output of the current build is more accurate.
|
||||
# Remove these deselects after the next stable release
|
||||
python3 -m pytest -q --collect-only --collect-upgrade-tests-to-file "$tmpfile" \
|
||||
-m 'allow_server_upgrade_test and not skip_server_upgrade_test' \
|
||||
--deselect test_schema_stitching.py::TestRemoteSchemaBasic::test_introspection \
|
||||
--deselect test_schema_stitching.py::TestAddRemoteSchemaCompareRootQueryFields::test_schema_check_arg_default_values_and_field_and_arg_types \
|
||||
--deselect test_graphql_mutations.py::TestGraphqlInsertPermission::test_user_with_no_backend_privilege \
|
||||
--deselect test_graphql_mutations.py::TestGraphqlInsertPermission::test_backend_user_no_admin_secret_fail \
|
||||
--deselect test_graphql_mutations.py::TestGraphqlMutationCustomSchema::test_update_article \
|
||||
--deselect test_graphql_queries.py::TestGraphQLQueryEnums::test_introspect_user_role \
|
||||
"${args[@]}" 1>/dev/null 2>/dev/null
|
||||
set +x
|
||||
cat "$tmpfile"
|
||||
cd - >/dev/null
|
||||
@ -174,11 +198,12 @@ run_server_upgrade_pytest() {
|
||||
set -x
|
||||
|
||||
# With --avoid-error-message-checks, we are only going to throw warnings if the error message has changed between releases
|
||||
# FIX ME: Remove the deselect below after the next stable release
|
||||
pytest --hge-urls "${HGE_URL}" --pg-urls "$HASURA_GRAPHQL_DATABASE_URL" \
|
||||
--avoid-error-message-checks "$@" \
|
||||
-m 'allow_server_upgrade_test and not skip_server_upgrade_test' \
|
||||
--deselect test_graphql_mutations.py::TestGraphqlUpdateBasic::test_numerics_inc \
|
||||
--deselect test_graphql_mutations.py::TestGraphqlInsertPermission::test_user_with_no_backend_privilege \
|
||||
--deselect test_graphql_mutations.py::TestGraphqlMutationCustomSchema::test_update_article \
|
||||
--deselect test_graphql_queries.py::TestGraphQLQueryEnums::test_introspect_user_role \
|
||||
-v $tests_to_run
|
||||
set +x
|
||||
cd -
|
||||
|
@ -191,9 +191,7 @@ pip3 install -r requirements.txt
|
||||
# node js deps
|
||||
curl -sL https://deb.nodesource.com/setup_8.x | bash -
|
||||
apt-get install -y nodejs
|
||||
npm_config_loglevel=error npm install $PYTEST_ROOT/remote_schemas/nodejs/
|
||||
|
||||
npm install apollo-server graphql
|
||||
(cd remote_schemas/nodejs && npm_config_loglevel=error npm ci)
|
||||
|
||||
mkdir -p "$OUTPUT_FOLDER/hpc"
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
###############################################################################
|
||||
## Configuration for auto-merge / auto-update bot
|
||||
##
|
||||
## See: https://kodiakhq.com/
|
||||
## See: https://kodiakhq.com/
|
||||
###############################################################################
|
||||
|
||||
# Kodiak's configuration file should be placed at `.kodiak.toml` (repository
|
||||
|
19
CHANGELOG.md
19
CHANGELOG.md
@ -2,16 +2,29 @@
|
||||
|
||||
## Next release
|
||||
|
||||
- server: allow remote relationships joining `type` column with `[type]` input argument as spec allows this coercion (fixes #5133)
|
||||
### Breaking changes
|
||||
|
||||
This release contains the [PDV refactor (#4111)](https://github.com/hasura/graphql-engine/pull/4111), a significant rewrite of the internals of the server, which did include some breaking changes:
|
||||
|
||||
- The semantics of explicit `null` values in `where` filters have changed according to the discussion in [issue 704](https://github.com/hasura/graphql-engine/issues/704#issuecomment-635571407): an explicit `null` value in a comparison input object will be treated as an error rather than resulting in the expression being evaluated to `True`. For instance: `delete_users(where: {id: {_eq: $userId}}) { name }` will yield an error if `$userId` is `null` instead of deleting all users.
|
||||
- The validation of required headers has been fixed (closing #14 and #3659):
|
||||
- if a query selects table `bar` through table `foo` via a relationship, the required permissions headers will be the union of the required headers of table `foo` and table `bar` (we used to only check the headers of the root table);
|
||||
- if an insert does not have an `on_conflict` clause, it will not require the update permissions headers.
|
||||
|
||||
### Bug fixes and improvements
|
||||
|
||||
(Add entries here in the order of: server, console, cli, docs, others)
|
||||
|
||||
- docs: add docs page on networking with docker (close #4346) (#4811)
|
||||
- server: some mutations that cannot be performed will no longer be in the schema (for instance, `delete_by_pk` mutations won't be shown to users that do not have select permissions on all primary keys) (#4111)
|
||||
- server: miscellaneous description changes (#4111)
|
||||
- server: treat the absence of `backend_only` configuration and `backend_only: false` equally (closing #5059) (#4111)
|
||||
- server: allow remote relationships joining `type` column with `[type]` input argument as spec allows this coercion (fixes #5133)
|
||||
- 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)
|
||||
|
||||
## `v1.3.1-beta.1`
|
||||
## `v1.3.1`, `v1.3.1-beta.1`
|
||||
|
||||
### Breaking change
|
||||
|
||||
|
@ -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;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { Connect } from 'react-redux';
|
||||
import styles from './RightContainer.scss';
|
||||
|
||||
const RightContainer: React.FC = ({ children }) => {
|
||||
@ -16,6 +15,4 @@ const RightContainer: React.FC = ({ children }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const rightContainerConnector = (connect: Connect) => connect()(RightContainer);
|
||||
|
||||
export default rightContainerConnector;
|
||||
export default RightContainer;
|
||||
|
@ -0,0 +1,3 @@
|
||||
import RightContainer from './RightContainer';
|
||||
|
||||
export { RightContainer };
|
@ -1,3 +0,0 @@
|
||||
import rightContainerConnector from './RightContainer/RightContainer';
|
||||
|
||||
export { rightContainerConnector };
|
@ -11,3 +11,6 @@
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
.react-toggle--focus .react-toggle-thumb {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import Tooltip from 'react-bootstrap/lib/Tooltip';
|
||||
import * as tooltips from './Tooltips';
|
||||
import globals from '../../Globals';
|
||||
import { getPathRoot } from '../Common/utils/urlUtils';
|
||||
|
||||
import Spinner from '../Common/Spinner/Spinner';
|
||||
import WarningSymbol from '../Common/WarningSymbol/WarningSymbol';
|
||||
import logo from './images/white-logo.svg';
|
||||
@ -46,7 +45,7 @@ import {
|
||||
import ToolTip from '../Common/Tooltip/Tooltip';
|
||||
import { setPreReleaseNotificationOptOutInDB } from '../../telemetry/Actions';
|
||||
import { Icon } from '../UIKit/atoms/Icon';
|
||||
import { ProPopup } from './components/ProPopup';
|
||||
import { Help, ProPopup } from './components/';
|
||||
|
||||
class Main extends React.Component {
|
||||
constructor(props) {
|
||||
@ -658,14 +657,7 @@ class Main extends React.Component {
|
||||
{getSettingsSelectedMarker()}
|
||||
</div>
|
||||
</Link>
|
||||
<a
|
||||
id="help"
|
||||
href="https://hasura.io/help"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<div className={styles.headerRightNavbarBtn}>HELP</div>
|
||||
</a>
|
||||
<Help isSelected={currentActiveBlock === 'support'} />
|
||||
{getLoveSection()}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -897,17 +897,14 @@
|
||||
.headerRightNavbarBtn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// background-color: #22283b;
|
||||
padding: 15px;
|
||||
position: relative;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
|
||||
&:hover {
|
||||
span {
|
||||
opacity: 0.7;
|
||||
font-weight: 600;
|
||||
}
|
||||
opacity: 0.7;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.selected {
|
||||
|
20
console/src/components/Main/components/Help.tsx
Normal file
20
console/src/components/Main/components/Help.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
import styles from '../Main.scss';
|
||||
|
||||
export const Help = ({ isSelected }: { isSelected: boolean }) => {
|
||||
return (
|
||||
<Link to="/support/forums/">
|
||||
<div className={styles.headerRightNavbarBtn}>
|
||||
HELP
|
||||
{isSelected ? (
|
||||
<span
|
||||
className={styles.selected}
|
||||
style={{ width: '90%', marginLeft: '5%' }}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
};
|
4
console/src/components/Main/components/index.ts
Normal file
4
console/src/components/Main/components/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { Help } from './Help';
|
||||
import { ProPopup } from './ProPopup';
|
||||
|
||||
export { Help, ProPopup };
|
@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="baseline-arrow_forward-24px" width="16" height="16" viewBox="0 0 16 16">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1{fill:none}.cls-2{fill:#303030}
|
||||
</style>
|
||||
</defs>
|
||||
<path id="Path_290" d="M0 0h16v16H0z" class="cls-1" data-name="Path 290"/>
|
||||
<path id="Path_291" d="M9.333 4l-.94.94 3.72 3.727H4V10h8.113l-3.72 3.727.94.94 5.333-5.333z" class="cls-2" data-name="Path 291" transform="translate(-1.333 -1.333)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 481 B |
3
console/src/components/Main/images/information.svg
Normal file
3
console/src/components/Main/images/information.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="prefix__streamline-icon-interface-alert-information-circle_16x16" width="12.5" height="12.5" data-name="streamline-icon-interface-alert-information-circle@16x16" viewBox="0 0 12.5 12.5">
|
||||
<path id="prefix__Path_5128" d="M6.25 0a6.25 6.25 0 1 0 6.25 6.25A6.25 6.25 0 0 0 6.25 0zm.781 9.375a.781.781 0 0 1-1.562 0v-2.5a.781.781 0 0 1 1.563 0zM6.25 5A1.25 1.25 0 1 1 7.5 3.75 1.25 1.25 0 0 1 6.25 5z" data-name="Path 5128" style="fill:#292822"/>
|
||||
</svg>
|
After Width: | Height: | Size: 497 B |
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Route, IndexRedirect } from 'react-router';
|
||||
import { rightContainerConnector } from '../../Common/Layout';
|
||||
import { RightContainer } from '../../Common/Layout/RightContainer';
|
||||
import Container from './Containers/Main';
|
||||
import { fetchActions } from './ServerIO';
|
||||
import globals from '../../../Globals';
|
||||
@ -36,7 +36,7 @@ const getActionsRouter = (connect, store, composeOnEnterHooks) => {
|
||||
onChange={actionsInit(store)}
|
||||
>
|
||||
<IndexRedirect to="manage" />
|
||||
<Route path="manage" component={rightContainerConnector(connect)}>
|
||||
<Route path="manage" component={RightContainer}>
|
||||
<IndexRedirect to="actions" />
|
||||
<Route path="actions" component={ActionsLandingPage(connect)} />
|
||||
<Route path="add" component={AddAction(connect)} />
|
||||
@ -51,7 +51,7 @@ const getActionsRouter = (connect, store, composeOnEnterHooks) => {
|
||||
component={ActionPermissions(connect)}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="types" component={rightContainerConnector(connect)}>
|
||||
<Route path="types" component={RightContainer}>
|
||||
<IndexRedirect to="manage" />
|
||||
<Route path="manage" component={TypesManage(connect)} />
|
||||
<Route path="relationships" component={TypesRelationships(connect)} />
|
||||
|
@ -110,7 +110,8 @@ export const getErrorMessage = (
|
||||
notificationMessage = error.code;
|
||||
}
|
||||
} else if ('internal' in error && 'error' in error.internal) {
|
||||
notificationMessage = `${error.code} : ${error.internal.error.message}`;
|
||||
notificationMessage = `${error.internal.error.message}.
|
||||
${error.internal.error.description}`;
|
||||
} else if ('custom' in error) {
|
||||
notificationMessage = error.custom;
|
||||
} else if ('code' in error && 'error' in error && 'path' in error) {
|
||||
|
@ -659,7 +659,10 @@ const makeMigrationCall = (
|
||||
}
|
||||
customOnSuccess(data, globals.consoleMode, currMigrationMode);
|
||||
};
|
||||
const retryMigration = (err = {}, errMsg = '') => {
|
||||
const retryMigration = (err = {}, errMsg = '', isPgCascade = false) => {
|
||||
const errorDetails = getErrorMessage('', err);
|
||||
const errorDetailsLines = errorDetails.split('\n');
|
||||
|
||||
dispatch(
|
||||
showNotification(
|
||||
{
|
||||
@ -667,8 +670,9 @@ const makeMigrationCall = (
|
||||
level: 'error',
|
||||
message: (
|
||||
<p>
|
||||
{getErrorMessage('', err)}
|
||||
<br />
|
||||
{errorDetailsLines.map((m, i) => (
|
||||
<div key={i}>{m}</div>
|
||||
))}
|
||||
<br />
|
||||
Do you want to drop the dependent items as well?
|
||||
</p>
|
||||
@ -680,7 +684,7 @@ const makeMigrationCall = (
|
||||
makeMigrationCall(
|
||||
dispatch,
|
||||
getState,
|
||||
cascadeUpQueries(upQueries), // cascaded new up queries
|
||||
cascadeUpQueries(upQueries, isPgCascade), // cascaded new up queries
|
||||
downQueries,
|
||||
migrationName,
|
||||
customOnSuccess,
|
||||
@ -689,6 +693,7 @@ const makeMigrationCall = (
|
||||
successMsg,
|
||||
errorMsg,
|
||||
shouldSkipSchemaReload,
|
||||
false,
|
||||
true // prevent further retry
|
||||
),
|
||||
},
|
||||
@ -700,8 +705,10 @@ const makeMigrationCall = (
|
||||
|
||||
const onError = err => {
|
||||
if (!isRetry) {
|
||||
const dependecyError = getDependencyError(err);
|
||||
if (dependecyError) return retryMigration(dependecyError, errorMsg);
|
||||
const { dependencyError, pgDependencyError } = getDependencyError(err);
|
||||
if (dependencyError) return retryMigration(dependencyError, errorMsg);
|
||||
if (pgDependencyError)
|
||||
return retryMigration(pgDependencyError, errorMsg, true);
|
||||
}
|
||||
|
||||
dispatch(handleMigrationErrors(errorMsg, err));
|
||||
|
@ -27,7 +27,7 @@ import {
|
||||
// metadataConnector,
|
||||
} from '.';
|
||||
|
||||
import { rightContainerConnector } from '../../Common/Layout';
|
||||
import { RightContainer } from '../../Common/Layout/RightContainer';
|
||||
|
||||
import {
|
||||
fetchDataInit,
|
||||
@ -51,7 +51,7 @@ const makeDataRouter = (
|
||||
return (
|
||||
<Route path="data" component={dataPageConnector(connect)}>
|
||||
<IndexRedirect to="schema/public" />
|
||||
<Route path="schema" component={rightContainerConnector(connect)}>
|
||||
<Route path="schema" component={RightContainer}>
|
||||
<IndexRedirect to="public" />
|
||||
<Route path=":schema" component={schemaConnector(connect)} />
|
||||
<Route path=":schema/tables" component={schemaConnector(connect)} />
|
||||
|
@ -692,7 +692,7 @@ class Schema extends Component {
|
||||
className={styles.add_mar_top}
|
||||
key={'non-trackable-custom-functions'}
|
||||
>
|
||||
<CollapsibleToggle title={heading} isOpen>
|
||||
<CollapsibleToggle title={heading}>
|
||||
<div className={`${styles.padd_left_remove} col-xs-12`}>
|
||||
{getNonTrackableFuncList()}
|
||||
</div>
|
||||
|
@ -112,11 +112,11 @@ class RelationshipEditor extends React.Component {
|
||||
<div className={styles.display_flex}>
|
||||
{getEditBtn()}
|
||||
<b className={styles.textNoNewLine}>{relName}</b>
|
||||
<GqlCompatibilityWarning
|
||||
identifier={relName}
|
||||
className={styles.add_mar_left_small}
|
||||
/>
|
||||
</div>
|
||||
<GqlCompatibilityWarning
|
||||
identifier={relName}
|
||||
className={styles.add_mar_left_small}
|
||||
/>
|
||||
<div className={tableStyles.relationshipTopPadding}>
|
||||
<p className={styles.textNoNewLine}>{getRelDef(relConfig)}</p>
|
||||
</div>
|
||||
|
@ -791,13 +791,23 @@ WHERE
|
||||
export const isColTypeString = colType =>
|
||||
['text', 'varchar', 'char', 'bpchar', 'name'].includes(colType);
|
||||
|
||||
export const cascadeUpQueries = (upQueries = []) =>
|
||||
const cascadePGSqlQuery = sql => {
|
||||
if (sql[sql.length - 1] === ';')
|
||||
return sql.substr(0, sql.length - 1) + ' CASCADE;';
|
||||
// SQL might have a " at the end
|
||||
else if (sql[sql.length - 2] === ';')
|
||||
return sql.substr(0, sql.length - 2) + ' CASCADE;';
|
||||
return sql + ' CASCADE;';
|
||||
};
|
||||
|
||||
export const cascadeUpQueries = (upQueries = [], isPgCascade = false) =>
|
||||
upQueries.map((i = {}) => {
|
||||
if (i.type === 'run_sql' || i.type === 'untrack_table') {
|
||||
return {
|
||||
...i,
|
||||
args: {
|
||||
...i.args,
|
||||
...(isPgCascade && { sql: cascadePGSqlQuery(i.args.sql) }),
|
||||
cascade: true,
|
||||
},
|
||||
};
|
||||
@ -808,14 +818,27 @@ export const cascadeUpQueries = (upQueries = []) =>
|
||||
export const getDependencyError = (err = {}) => {
|
||||
if (err.code == ERROR_CODES.dependencyError.code) {
|
||||
// direct dependency error
|
||||
return err;
|
||||
} else if (err.code == ERROR_CODES.dataApiError.code) {
|
||||
// message is coming as error, further parssing willbe based on message key
|
||||
const actualError = isJsonString(err.message)
|
||||
? JSON.parse(err.message)
|
||||
: {};
|
||||
if (actualError.code == ERROR_CODES.dependencyError.code) {
|
||||
return { ...actualError, message: actualError.error };
|
||||
}
|
||||
return { dependencyError: err };
|
||||
}
|
||||
if (err.code == ERROR_CODES.dataApiError.code) {
|
||||
// with CLI mode, error is getting as a string with the key `message`
|
||||
err = isJsonString(err.message) ? JSON.parse(err.message) : {};
|
||||
}
|
||||
|
||||
if (err.code == ERROR_CODES.dependencyError.code)
|
||||
return {
|
||||
dependencyError: { ...err, message: err.error },
|
||||
};
|
||||
if (
|
||||
err.code === ERROR_CODES.postgresError.code &&
|
||||
err?.internal?.error?.status_code === '2BP01' // pg dependent error > https://www.postgresql.org/docs/current/errcodes-appendix.html
|
||||
)
|
||||
return {
|
||||
pgDependencyError: {
|
||||
...err,
|
||||
message: `${err?.internal?.error?.message}:\n
|
||||
${err?.internal?.error?.description || ''}`,
|
||||
},
|
||||
};
|
||||
return {};
|
||||
};
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Connect } from 'react-redux';
|
||||
import { Route, IndexRedirect, EnterHook, RouterState } from 'react-router';
|
||||
import rightContainerConnector from '../../Common/Layout/RightContainer/RightContainer';
|
||||
import Container from './Container';
|
||||
import { fetchTriggers } from './ServerIO';
|
||||
import globals from '../../../Globals';
|
||||
@ -52,6 +51,7 @@ import {
|
||||
AdhocEventLogs,
|
||||
AdhocEventsInfo,
|
||||
} from './AdhocEvents';
|
||||
import { RightContainer } from '../../Common/Layout/RightContainer';
|
||||
|
||||
const triggersInit = (dispatch: Dispatch): EnterHook => {
|
||||
return (
|
||||
@ -87,10 +87,7 @@ const getTriggersRouter = (
|
||||
onEnter={composeOnEnterHooks([triggersInit(store.dispatch)])}
|
||||
>
|
||||
<IndexRedirect to={dataEventsPrefix} />
|
||||
<Route
|
||||
path={dataEventsPrefix}
|
||||
component={rightContainerConnector(connect)}
|
||||
>
|
||||
<Route path={dataEventsPrefix} component={RightContainer}>
|
||||
<IndexRedirect to={getDataEventsLandingRoute('relative')} />
|
||||
<Route path={getAddETRoute('relative')} component={AddEventTrigger} />
|
||||
<Route
|
||||
@ -114,10 +111,7 @@ const getTriggersRouter = (
|
||||
component={EventTriggerLanding}
|
||||
/>
|
||||
</Route>
|
||||
<Route
|
||||
path={scheduledEventsPrefix}
|
||||
component={rightContainerConnector(connect)}
|
||||
>
|
||||
<Route path={scheduledEventsPrefix} component={RightContainer}>
|
||||
<IndexRedirect to={getScheduledEventsLandingRoute('relative')} />
|
||||
<Route
|
||||
path={getAddSTRoute('relative')}
|
||||
@ -144,10 +138,7 @@ const getTriggersRouter = (
|
||||
component={ScheduledTriggeModify}
|
||||
/>
|
||||
</Route>
|
||||
<Route
|
||||
path={adhocEventsPrefix}
|
||||
component={rightContainerConnector(connect)}
|
||||
>
|
||||
<Route path={adhocEventsPrefix} component={RightContainer}>
|
||||
<IndexRedirect to={getAdhocEventsInfoRoute('relative')} />
|
||||
<Route
|
||||
path={getAddAdhocEventRoute('relative')}
|
||||
|
@ -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();
|
||||
},
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Route, IndexRedirect } from 'react-router';
|
||||
import { rightContainerConnector } from '../../Common/Layout';
|
||||
import { RightContainer } from '../../Common/Layout/RightContainer';
|
||||
import globals from '../../../Globals';
|
||||
import {
|
||||
remoteSchemaPageConnector,
|
||||
@ -95,7 +95,7 @@ const getRemoteSchemaRouter = (connect, store, composeOnEnterHooks) => {
|
||||
onChange={fetchInitialData(store)}
|
||||
>
|
||||
<IndexRedirect to="manage" />
|
||||
<Route path="manage" component={rightContainerConnector(connect)}>
|
||||
<Route path="manage" component={RightContainer}>
|
||||
<IndexRedirect to="schemas" />
|
||||
<Route path="schemas" component={landingConnector(connect)} />
|
||||
<Route path="add" component={addConnector(connect)} />
|
||||
|
@ -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;
|
||||
|
@ -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 });
|
||||
|
@ -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(
|
||||
|
@ -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>
|
||||
|
109
console/src/components/Services/Support/HelpPage.tsx
Normal file
109
console/src/components/Services/Support/HelpPage.tsx
Normal file
@ -0,0 +1,109 @@
|
||||
import React from 'react';
|
||||
|
||||
import styles from './Support.scss';
|
||||
import discord from './images/discord.svg';
|
||||
import docs from './images/docs.svg';
|
||||
import stackOverflow from './images/stack-overflow.svg';
|
||||
import github from './images/github.svg';
|
||||
|
||||
const CHECK_FORUMS = `
|
||||
If you need any help with developing on Hasura, you can check out
|
||||
these various Hasura forums. Our community members include some very
|
||||
experienced engineers from some of the world’s most exciting
|
||||
companies, and many of them have been using Hasura in production for a
|
||||
long time.`;
|
||||
|
||||
const supportListState = [
|
||||
{
|
||||
brand: discord,
|
||||
title: 'Discord',
|
||||
description:
|
||||
'Our community hangs out here. Join discord to ask/help folks in the community.',
|
||||
link: 'https://discord.com/invite/hasura',
|
||||
},
|
||||
{
|
||||
brand: docs,
|
||||
title: 'Docs',
|
||||
description: 'Head to docs to search for what you’re looking for.',
|
||||
link: 'https://hasura.io/docs/',
|
||||
},
|
||||
{
|
||||
brand: stackOverflow,
|
||||
title: 'StackOverflow',
|
||||
description: 'Ask your Hasura questions here and tag as ‘hasura’',
|
||||
link: 'https://stackoverflow.com/questions/tagged/hasura',
|
||||
},
|
||||
{
|
||||
brand: github,
|
||||
title: 'GitHub',
|
||||
description:
|
||||
'Create an issue on GitHub to report bugs, suggest improvements or give us a star!',
|
||||
link: 'https://github.com/hasura/graphql-engine/',
|
||||
},
|
||||
];
|
||||
|
||||
const HelpPage = () => {
|
||||
return (
|
||||
<div
|
||||
className={`${styles.padd_left_remove} ${styles.supportForumWrapper} container-fluid ${styles.padd_top}`}
|
||||
>
|
||||
<div className={styles.padd_left}>
|
||||
<h2 className={`${styles.headerText} ${styles.inline_block}`}>
|
||||
Support Forums
|
||||
</h2>
|
||||
<div className={`${styles.descriptionText} ${styles.wd60}`}>
|
||||
{CHECK_FORUMS}
|
||||
</div>
|
||||
<div className={styles.supportWrapper}>
|
||||
{supportListState.map((list, index) => {
|
||||
return (
|
||||
<div
|
||||
className={`col-md-6 col-sm-6 col-xs-12 ${styles.padd_remove} ${styles.supportDisplay}`}
|
||||
>
|
||||
<a
|
||||
key={index}
|
||||
href={list.link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.supportFlex}
|
||||
>
|
||||
<div className={styles.supportList}>
|
||||
<div className={styles.supportBrand}>
|
||||
<img src={list.brand} alt={list.title} />
|
||||
</div>
|
||||
<div className={styles.supportContainer}>
|
||||
<div className={styles.title}>{list.title}</div>
|
||||
<div className={styles.descriptionText}>
|
||||
{list.description}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className={`${styles.descriptionText} ${styles.wd60}`}>
|
||||
If you would like to talk to our Product Specialists, head to our{' '}
|
||||
<a
|
||||
href="https://hasura.io/help"
|
||||
// eslint-disable-next-line react/jsx-no-target-blank
|
||||
target="_blank"
|
||||
>
|
||||
help page
|
||||
</a>{' '}
|
||||
to chat with us or{' '}
|
||||
<a
|
||||
href="https://calendly.com/hasura/prod-expert-call"
|
||||
// eslint-disable-next-line react/jsx-no-target-blank
|
||||
target="_blank"
|
||||
>
|
||||
set up a call
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default HelpPage;
|
60
console/src/components/Services/Support/Support.scss
Normal file
60
console/src/components/Services/Support/Support.scss
Normal file
@ -0,0 +1,60 @@
|
||||
@import '../../Common/Common.scss';
|
||||
|
||||
.supportForumWrapper {
|
||||
.headerText {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.supportWrapper {
|
||||
padding-top: 40px;
|
||||
padding-bottom: 32px;
|
||||
width: 75%;
|
||||
display: flex;
|
||||
list-style: none;
|
||||
flex-wrap: wrap;
|
||||
clear: both;
|
||||
.supportDisplay {
|
||||
display: flex;
|
||||
}
|
||||
.supportFlex {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
.supportList {
|
||||
padding: 22px;
|
||||
width: calc(100% - 15px);
|
||||
margin-right: 15px;
|
||||
margin-bottom: 15px;
|
||||
border-radius: 4px;
|
||||
border: solid 1px #bbbbbb;
|
||||
display: flex;
|
||||
flex: 1 0 auto;
|
||||
.supportBrand {
|
||||
padding-right: 10px;
|
||||
}
|
||||
.supportContainer {
|
||||
.title {
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
color: #303030;
|
||||
border-bottom: 1px solid transparent;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
.descriptionText {
|
||||
font-weight: normal;
|
||||
color: #303030;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
background-color: #ecf0f2;
|
||||
.title {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
29
console/src/components/Services/Support/SupportContainer.tsx
Normal file
29
console/src/components/Services/Support/SupportContainer.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import { RightContainer } from '../../Common/Layout/RightContainer';
|
||||
import LeftContainer from '../../Common/Layout/LeftContainer/LeftContainer';
|
||||
import PageContainer from '../../Common/Layout/PageContainer/PageContainer';
|
||||
|
||||
import styles from '../../Common/TableCommon/Table.scss';
|
||||
|
||||
const helmetTitle = 'Support Forums | Hasura';
|
||||
|
||||
const LeftBar = () => (
|
||||
<LeftContainer>
|
||||
<ul>
|
||||
<li role="presentation" className={styles.active}>
|
||||
<Link className={styles.linkBorder} to="/support/forums/">
|
||||
Support Forums
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</LeftContainer>
|
||||
);
|
||||
|
||||
export const SupportContainer: React.FC = ({ children }) => {
|
||||
return (
|
||||
<PageContainer helmet={helmetTitle} leftContainer={<LeftBar />}>
|
||||
<RightContainer>{children}</RightContainer>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<g id="prefix__Group_8285" data-name="Group 8285" transform="translate(.334)">
|
||||
<path id="prefix__discord-brands" d="M13.474 11.025a1.188 1.188 0 1 1-1.184-1.288 1.236 1.236 0 0 1 1.184 1.288zm-5.42-1.288a1.293 1.293 0 0 0 0 2.576 1.236 1.236 0 0 0 1.184-1.288 1.229 1.229 0 0 0-1.184-1.288zm12.255-7.346V23.21c-2.924-2.584-1.989-1.728-5.385-4.886l.615 2.147H2.379A2.385 2.385 0 0 1 0 18.081V2.391A2.385 2.385 0 0 1 2.379 0H17.93a2.385 2.385 0 0 1 2.379 2.391zM17 13.392a15.541 15.541 0 0 0-1.67-6.765 5.739 5.739 0 0 0-3.261-1.219l-.162.186A7.724 7.724 0 0 1 14.8 7.068a9.861 9.861 0 0 0-8.669-.337c-.429.2-.685.337-.685.337a7.829 7.829 0 0 1 3.052-1.52l-.116-.139a5.739 5.739 0 0 0-3.264 1.218 15.541 15.541 0 0 0-1.671 6.766 4.208 4.208 0 0 0 3.54 1.764s.429-.522.778-.963a3.609 3.609 0 0 1-2.031-1.369c.171.119.452.274.476.29a8.459 8.459 0 0 0 7.242.406 6.641 6.641 0 0 0 1.335-.685 3.662 3.662 0 0 1-2.1 1.381c.348.441.766.94.766.94A4.242 4.242 0 0 0 17 13.392z" transform="translate(1.762 .146)" style="fill:#505050"/>
|
||||
<path id="prefix__Rectangle_4799" d="M0 0H24V24H0z" data-name="Rectangle 4799" transform="translate(-.334)" style="fill:none"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
14
console/src/components/Services/Support/images/docs.svg
Normal file
14
console/src/components/Services/Support/images/docs.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<defs>
|
||||
<style>
|
||||
.prefix__cls-1{fill:none;stroke:#505050;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="prefix__streamline-icon-paginate-filter-text_24x24" data-name="streamline-icon-paginate-filter-text@24x24" transform="translate(0 .004)">
|
||||
<path id="prefix__Path_5887" d="M3.75.746h19.5v19.5H3.75z" class="prefix__cls-1" data-name="Path 5887"/>
|
||||
<path id="prefix__Path_5888" d="M20.25 23.246h-18a1.5 1.5 0 0 1-1.5-1.5v-18" class="prefix__cls-1" data-name="Path 5888"/>
|
||||
<path id="prefix__Path_5889" d="M8.25 6.746h10.5" class="prefix__cls-1" data-name="Path 5889"/>
|
||||
<path id="prefix__Path_5890" d="M8.25 9.746h10.5" class="prefix__cls-1" data-name="Path 5890"/>
|
||||
<path id="prefix__Path_5891" d="M8.25 12.746h7.5" class="prefix__cls-1" data-name="Path 5891"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 976 B |
@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24.021" height="23.585" viewBox="0 0 24.021 23.585">
|
||||
<g id="prefix__streamline-icon-developer-community-github-1_24x24_1_" data-name="streamline-icon-developer-community-github-1@24x24 (1)" transform="translate(.08 -.021)">
|
||||
<path id="prefix__Path_5872" d="M14.41 22.35a.5.5 0 0 0 .64.48 11.25 11.25 0 1 0-6.24 0 .5.5 0 0 0 .64-.48V20A2.81 2.81 0 0 1 6 18.28 6.07 6.07 0 0 0 4.64 16c2.85.69 2.9 2.54 4.84 1.67a4 4 0 0 1 .63-1.82c-2.2-.25-4.52-.6-4.52-4.4a3.84 3.84 0 0 1 1-2.66 3.56 3.56 0 0 1 .1-2.62s.83-.27 2.73 1a9.39 9.39 0 0 1 5 0c1.89-1.28 2.72-1 2.72-1a3.56 3.56 0 0 1 .1 2.62 3.83 3.83 0 0 1 1 2.66c0 3.81-2.32 4.15-4.53 4.39a3.83 3.83 0 0 1 .68 2.33z" data-name="Path 5872" style="fill:none;stroke:#505050;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 855 B |
@ -0,0 +1,16 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20.075" height="23.735" viewBox="0 0 20.075 23.735">
|
||||
<defs>
|
||||
<style>
|
||||
.prefix__cls-1{fill:none;stroke:#505050;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="prefix__streamline-icon-developer-community-stack-overflow_24x24" data-name="streamline-icon-developer-community-stack-overflow@24x24" transform="translate(-2.1 -.015)">
|
||||
<path id="prefix__Path_5873" d="M17.85 14.46V21a2 2 0 0 1-2 2h-11a2 2 0 0 1-2-2v-6.5" class="prefix__cls-1" data-name="Path 5873"/>
|
||||
<path id="prefix__Path_5874" d="M14.25 18.96h-8" class="prefix__cls-1" data-name="Path 5874"/>
|
||||
<path id="prefix__Path_5875" d="M14.63 16.27l-7.82-1.66" class="prefix__cls-1" data-name="Path 5875"/>
|
||||
<path id="prefix__Path_5876" d="M15.57 13.72l-7.31-3.24" class="prefix__cls-1" data-name="Path 5876"/>
|
||||
<path id="prefix__Path_5877" d="M17.01 11.42l-6.48-4.69" class="prefix__cls-1" data-name="Path 5877"/>
|
||||
<path id="prefix__Path_5878" d="M18.9 9.48l-5.36-5.94" class="prefix__cls-1" data-name="Path 5878"/>
|
||||
<path id="prefix__Path_5879" d="M21.15 7.96l-4.01-6.92" class="prefix__cls-1" data-name="Path 5879"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
@ -81,10 +81,6 @@ const Html: React.FC<HtmlProps> = props => {
|
||||
|
||||
<div id="content" className="content" />
|
||||
<script src={`${assets.javascript.main}`} charSet="UTF-8" />
|
||||
{/*
|
||||
<script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.13.1/build/highlight.min.js" />
|
||||
<script type="text/javascript" src="https://unpkg.com/sql-formatter@latest/dist/sql-formatter.min.js" />
|
||||
*/}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
31
console/src/hooks/useOnClickOutside.ts
Normal file
31
console/src/hooks/useOnClickOutside.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
const MOUSEDOWN = 'mousedown';
|
||||
const TOUCHSTART = 'touchstart';
|
||||
|
||||
type HandledEvents = [typeof MOUSEDOWN, typeof TOUCHSTART];
|
||||
type HandledEventsType = HandledEvents[number];
|
||||
type PossibleEvent = {
|
||||
[Type in HandledEventsType]: HTMLElementEventMap[Type];
|
||||
}[HandledEventsType];
|
||||
type Handler = (event: PossibleEvent) => void;
|
||||
|
||||
export const useOnClickOutside = (
|
||||
ref: React.RefObject<HTMLElement>,
|
||||
handler: Handler
|
||||
) => {
|
||||
useEffect(() => {
|
||||
const listener = (event: PossibleEvent) => {
|
||||
if (!ref.current || ref.current.contains(event.target as Node)) {
|
||||
return;
|
||||
}
|
||||
handler(event);
|
||||
};
|
||||
document.addEventListener('mousedown', listener);
|
||||
document.addEventListener('touchstart', listener);
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', listener);
|
||||
document.removeEventListener('touchstart', listener);
|
||||
};
|
||||
}, [ref, handler]);
|
||||
};
|
10
console/src/hooks/useToggle.ts
Normal file
10
console/src/hooks/useToggle.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export const useToggle = (
|
||||
initialValue: boolean
|
||||
): [boolean, () => void, React.Dispatch<React.SetStateAction<boolean>>] => {
|
||||
const [value, setValue] = useState(initialValue);
|
||||
const toggleValue = () => setValue(prev => !prev);
|
||||
|
||||
return [value, toggleValue, setValue];
|
||||
};
|
@ -40,6 +40,8 @@ import { showErrorNotification } from './components/Services/Common/Notification
|
||||
import { CLI_CONSOLE_MODE } from './constants';
|
||||
import UIKit from './components/UIKit/';
|
||||
import { Heading } from './components/UIKit/atoms';
|
||||
import { SupportContainer } from './components/Services/Support/SupportContainer';
|
||||
import HelpPage from './components/Services/Support/HelpPage';
|
||||
|
||||
const routes = store => {
|
||||
// load hasuractl migration status
|
||||
@ -71,7 +73,6 @@ const routes = store => {
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
const _dataRouterUtils = dataRouterUtils(connect, store, composeOnEnterHooks);
|
||||
const requireSchema = _dataRouterUtils.requireSchema;
|
||||
const dataRouter = _dataRouterUtils.makeDataRouter;
|
||||
@ -146,6 +147,9 @@ const routes = store => {
|
||||
{actionsRouter}
|
||||
{eventsRouter}
|
||||
{uiKitRouter}
|
||||
<Route path="support" component={SupportContainer}>
|
||||
<Route path="forums" component={HelpPage} />
|
||||
</Route>
|
||||
</Route>
|
||||
</Route>
|
||||
<Route path="404" component={PageNotFound} status="404" />
|
||||
|
@ -1,5 +1,7 @@
|
||||
.. title:: 404 - Page Not Found
|
||||
|
||||
:orphan:
|
||||
|
||||
404 - Page Not Found
|
||||
---------------------
|
||||
|
||||
|
4
docs/_static/scripts/hdocs.js
vendored
4
docs/_static/scripts/hdocs.js
vendored
@ -14,8 +14,8 @@ window.hdocs = (function () {
|
||||
|
||||
docsearch({
|
||||
appId: 'WCBB1VVLRC',
|
||||
apiKey: '298d448cd9d7ed93fbab395658da19e8',
|
||||
indexName: 'graphql-docs-prod',
|
||||
apiKey: HDOCS_ALGOLIA_API_KEY,
|
||||
indexName: HDOCS_ALGOLIA_INDEX,
|
||||
inputSelector: '#search_element',
|
||||
transformData: hdocs.transformSearchData,
|
||||
debug: false
|
||||
|
6
docs/_static/styles/landing.css
vendored
6
docs/_static/styles/landing.css
vendored
@ -15,13 +15,11 @@
|
||||
}
|
||||
|
||||
.body_content {
|
||||
font-family: 'Gudea';
|
||||
font-size: 15px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.small_content {
|
||||
font-family: 'Gudea';
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
@ -101,6 +99,10 @@
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.text_left {
|
||||
text-align: left;
|
||||
}
|
||||
|
4
docs/_static/styles/main.css
vendored
4
docs/_static/styles/main.css
vendored
@ -640,6 +640,10 @@ article ol ol {
|
||||
margin: 15px;
|
||||
}
|
||||
|
||||
.sphinxsidebarwrapper > ul:not(.current) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
background-color: #001934;
|
||||
width: 24% !important;
|
||||
|
20
docs/_theme/djangodocs/layout.html
vendored
20
docs/_theme/djangodocs/layout.html
vendored
@ -4,6 +4,16 @@
|
||||
{% set is_landing_page = true %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if pagename.startswith('graphql/core') %}
|
||||
{% set is_core = true %}
|
||||
{% set ALGOLIA_INDEX = 'graphql-docs-prod' %}
|
||||
{% set ALGOLIA_API_KEY = '298d448cd9d7ed93fbab395658da19e8' %}
|
||||
{%- elif pagename.startswith('graphql/cloud') %}
|
||||
{% set is_cloud = true %}
|
||||
{% set ALGOLIA_INDEX = 'cloud-docs-prod' %}
|
||||
{% set ALGOLIA_API_KEY = 'cf84f05a225bedb72ce472dada63d29f' %}
|
||||
{%- endif %}
|
||||
|
||||
{% set css_files = css_files + ['_static/graphiql/graphiql.css', '_static/styles/main.css'] %}
|
||||
|
||||
{%- if is_landing_page %}
|
||||
@ -105,7 +115,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href='https://hasura.io/docs/1.0/graphql/manual/getting-started/index.html'>
|
||||
<a href='https://hasura.io/docs/1.0/graphql/core/getting-started/index.html'>
|
||||
<button class="commonBtn navBtnHome">
|
||||
Get Started
|
||||
</button>
|
||||
@ -162,7 +172,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="menuLink" href='https://hasura.io/docs/1.0/graphql/manual/getting-started/index.html'>
|
||||
<a class="menuLink" href='https://hasura.io/docs/1.0/graphql/core/getting-started/index.html'>
|
||||
<button class='commonBtn navBtnHome'>
|
||||
Get Started
|
||||
</button>
|
||||
@ -187,12 +197,12 @@
|
||||
<img src="{{ pathto('_images/layout/close-icon.svg', 1) }}" alt="Close"/>
|
||||
</div>
|
||||
<div class="tabbarContainerWrapper blueBgColor boderBottom">
|
||||
<a href="" class="tabbarTabActive">
|
||||
<a href="{{ pathto('graphql/core/index.html', 1) }}" {%- if is_core %} class="tabbarTabActive" {%- endif %}>
|
||||
<span>
|
||||
Hasura Core
|
||||
</span>
|
||||
</a>
|
||||
<a href="https://hasura.io/docs/cloud/1.0/manual/index.html">
|
||||
<a href="{{ pathto('graphql/cloud/index.html', 1) }}" {%- if is_cloud %} class="tabbarTabActive" {%- endif %}>
|
||||
<span>
|
||||
Hasura Cloud
|
||||
</span>
|
||||
@ -344,6 +354,8 @@
|
||||
<script type="text/javascript">
|
||||
const HDOCS_BASE_DOMAIN = "{{ BASE_DOMAIN }}";
|
||||
const HDOCS_GRAPHIQL_DEFAULT_ENDPOINT = "{{ GRAPHIQL_DEFAULT_ENDPOINT }}";
|
||||
const HDOCS_ALGOLIA_INDEX = "{{ALGOLIA_INDEX}}";
|
||||
const HDOCS_ALGOLIA_API_KEY = "{{ALGOLIA_API_KEY}}";
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
hdocs && hdocs.setup();
|
||||
|
21
docs/_theme/djangodocs/localtoc.html
vendored
21
docs/_theme/djangodocs/localtoc.html
vendored
@ -1,24 +1,3 @@
|
||||
{%- if display_toc %}
|
||||
<!--
|
||||
<div class="header_main_logo inline-block mobile-hide">
|
||||
<a href="https://{{ BASE_DOMAIN }}/" target="_blank" rel="noopener">
|
||||
<div class="img_wrapper inline-block">
|
||||
<img class="responsive logo_img" src="{{ pathto('_images/layout/logo-lite.svg', 1) }}" alt="Hasura Logo Light" />
|
||||
</div>
|
||||
</a>
|
||||
<a class="docs_label" href="{{ pathto('', 1) }}">
|
||||
<div class="inline-block hero"> docs </div>
|
||||
</a>
|
||||
<a class="version_txt">
|
||||
<select value="{{ version }}" onchange="location = this.value;" class="selected" aria-label="Select Version">
|
||||
{%- if version == '1.0' %}
|
||||
<option class="option_val" value="https://{{ BASE_DOMAIN }}/docs/1.0/graphql/manual/index.html" selected="selected">v1.x</option>
|
||||
{%- else -%}
|
||||
<option class="option_val" value="https://{{ BASE_DOMAIN }}/docs/1.0/graphql/manual/index.html">v1.x</option>
|
||||
{% endif %}
|
||||
</select>
|
||||
</a>
|
||||
</div>
|
||||
-->
|
||||
{{ toc_full }}
|
||||
{%- endif %}
|
||||
|
32
docs/_theme/djangodocs/pages/landing.html
vendored
32
docs/_theme/djangodocs/pages/landing.html
vendored
@ -5,25 +5,23 @@
|
||||
<div class="box_head_wrapper">
|
||||
<div class="box_head">
|
||||
<img src="{{ pathto('_images/landing/graphql.svg', 1) }}" alt="GraphQL engine"/>
|
||||
<h3 class="head_wrapper">1. The Hasura GraphQL engine</h3>
|
||||
</div>
|
||||
<div class="view_all_wrapper small_content">
|
||||
<h3 class="head_wrapper">Hasura docs</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="small_content space_wrapper text_left">
|
||||
This guide covers all Hasura GraphQL engine concepts and features.
|
||||
<br/> <br/>
|
||||
</div>
|
||||
<div class="sign_in_wrapper space_wrapper">
|
||||
<div class="get_start">
|
||||
<img src="{{ pathto('_images/landing/manual.svg', 1) }}" alt="Manual"/>
|
||||
</div>
|
||||
<a href="{{ pathto('graphql/manual/index.html', 1) }}">
|
||||
<div class="docs_link body_content">
|
||||
Open manual
|
||||
<img src="{{ pathto('_images/landing/right-arrow.svg', 1) }}" alt="Right Arrow" />
|
||||
</div>
|
||||
</a>
|
||||
<div class="space_wrapper text_left">
|
||||
<p class="description">This guide covers all Hasura concepts and features.</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a class="docs_link" href="{{ pathto('graphql/core/index.html', 1) }}">
|
||||
Core docs
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="docs_link" href="{{ pathto('graphql/cloud/index.html', 1) }}">
|
||||
Cloud docs
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
34
docs/graphql/cloud/allow-lists.rst
Normal file
34
docs/graphql/cloud/allow-lists.rst
Normal file
@ -0,0 +1,34 @@
|
||||
.. meta::
|
||||
:description: Hasura Cloud allow lists
|
||||
:keywords: hasura, docs, cloud, security, allow
|
||||
|
||||
.. _allow_lists:
|
||||
|
||||
Allow lists
|
||||
===========
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
You can specify a list of safe operations (GraphQL queries, mutations or subscriptions) for your project. This list restricts your project's GraphQL Engine to execute only queries that are present in the list.
|
||||
|
||||
Manage the allow list
|
||||
---------------------
|
||||
|
||||
The manager view offers inspection, export, or removal of operations in the allow list:
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/security/pro-tabs-allowlist.png
|
||||
:alt: Hasura Cloud Console allow list tab
|
||||
|
||||
Quick-create allowed operations
|
||||
-------------------------------
|
||||
|
||||
This Pro feature lets you add to the allow list with one click from the record of past operations. (With Core, allow lists must be :ref:`managed manually <allow_list>`.)
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/security/allowlist-add-new-op.png
|
||||
:alt: Hasura Cloud Console create new allowed operation
|
49
docs/graphql/cloud/api-limits.rst
Normal file
49
docs/graphql/cloud/api-limits.rst
Normal file
@ -0,0 +1,49 @@
|
||||
.. meta::
|
||||
:description: Hasura Cloud API limits
|
||||
:keywords: hasura, docs, cloud, security, limits
|
||||
|
||||
.. _api_limits:
|
||||
|
||||
API limits
|
||||
==========
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Limiting the depth and/or rate of API requests can help prevent API performance issues caused by malicious or poorly implemented queries.
|
||||
|
||||
Configuring an API limit
|
||||
------------------------
|
||||
|
||||
**Rate limits**
|
||||
Restricts number of GraphQL operations per minute. This uses a sliding window approach. This means whenever Hasura Pro receives a request, it will count the rate of that client starting from the current time to last one minute.
|
||||
|
||||
**Depth limits**
|
||||
Restricts a GraphQL operation based on its depth, preventing deeply nested queries.
|
||||
|
||||
API limits are defined by **role** (anonymous, user) and can restrict request rate, depth, or both. Unique request parameters can include IP address or session variables (*x-hasura-user-id*, *x-hasura-org-id*, etc.)
|
||||
|
||||
Manage API limits
|
||||
-----------------
|
||||
|
||||
API limits can have a *global* or *per role* configuration. If an incoming request does not contain a valid role then the global limit is applied.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/security/pro-tab-apilimits.png
|
||||
:alt: Hasura Cloud Console api limit tab
|
||||
|
||||
.. admonition:: Admin & IntrospectionQuery exemptions
|
||||
|
||||
All API limits are **not** applied for the admin role, and depth limits are **NOT** applied to introspection queries
|
||||
|
||||
Quick-create limits
|
||||
-------------------
|
||||
|
||||
Hasura Cloud lets you add limits with one click from the list of past operations.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/security/pro-tab-apilimit-config.png
|
||||
:alt: Hasura Cloud Console create new api limit
|
140
docs/graphql/cloud/api-reference.rst
Normal file
140
docs/graphql/cloud/api-reference.rst
Normal file
@ -0,0 +1,140 @@
|
||||
.. meta::
|
||||
:description: Hasura Cloud API reference
|
||||
:keywords: hasura, cloud, docs, API, API reference
|
||||
|
||||
.. _cloud_api_reference:
|
||||
|
||||
API Reference
|
||||
=============
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Hasura Cloud provides a GraphQL API to interact with the services to create
|
||||
and manage your projects.
|
||||
|
||||
You can use any GraphQL client and use the API with the right authentication header.
|
||||
|
||||
.. note::
|
||||
|
||||
Please note that the API is still in beta and might change before the
|
||||
stable release.
|
||||
|
||||
Endpoint
|
||||
--------
|
||||
|
||||
API endpoint is ``https://data.pro.hasura.io/v1/graphql``.
|
||||
|
||||
Authentication
|
||||
--------------
|
||||
|
||||
Authentication is done using a Personal Access Token that you can create from
|
||||
the Hasura Cloud Dashboard. You can find this option under the "Settings" tab.
|
||||
|
||||
Once you have the token it can be used with the header:
|
||||
``Authorization: pat <token>``.
|
||||
|
||||
.. note::
|
||||
|
||||
This token can be used to authenticate against Hasura Cloud APIs and your Hasura Cloud projects.
|
||||
Make sure you keep it secure. The token will be valid until you delete it from the dashboard.
|
||||
|
||||
APIs
|
||||
----
|
||||
|
||||
Each Hasura Cloud project is backed by an API entity called "Tenant", with a
|
||||
distinct "Tenant ID" which is different from "Project ID". Each Project is
|
||||
associated with a Tenant. In some cases, like Metrics API, the Project ID is
|
||||
used instead of Tenant ID.
|
||||
|
||||
List of APIs:
|
||||
|
||||
.. contents::
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Create a Tenant
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: graphql
|
||||
|
||||
mutation createProject {
|
||||
createTenant(
|
||||
cloud: "aws"
|
||||
region: "us-east-2"
|
||||
databaseUrl: "postgres://username:password@database-host.com:5432/dbname"
|
||||
) {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
|
||||
Get Tenant details
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: graphql
|
||||
|
||||
query getTenantDetails {
|
||||
tenant_by_pk(
|
||||
id: "7a79cf94-0e53-4520-a560-1b02bf522f08"
|
||||
) {
|
||||
id
|
||||
slug
|
||||
project {
|
||||
id
|
||||
endpoint
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Delete a Tenant
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: graphql
|
||||
|
||||
mutation deleteTenant {
|
||||
deleteTenant(
|
||||
tenantId: "7a79cf94-0e53-4520-a560-1b02bf522f08"
|
||||
) {
|
||||
status
|
||||
}
|
||||
}
|
||||
|
||||
Get ENV Vars
|
||||
^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: graphql
|
||||
|
||||
query getTenantENV {
|
||||
getTenantEnv(
|
||||
tenantId: "7a79cf94-0e53-4520-a560-1b02bf522f08"
|
||||
) {
|
||||
hash
|
||||
envVars
|
||||
}
|
||||
}
|
||||
|
||||
Update ENV Vars
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: graphql
|
||||
|
||||
mutation updateTenantEnv {
|
||||
updateTenantEnv(
|
||||
tenantId: "7a79cf94-0e53-4520-a560-1b02bf522f08"
|
||||
currentHash: "6902a395d70072fbf8d36288f0eacc36c9d82e68"
|
||||
envs: [
|
||||
{key: "HASURA_GRAPHQL_ENABLE_CONSOLE", value: "true"},
|
||||
{key: "ACTIONS_ENDPOINT", value: "https://my-actions-endpoint.com/actions"}
|
||||
]
|
||||
) {
|
||||
hash
|
||||
envVars
|
||||
}
|
||||
}
|
93
docs/graphql/cloud/getting-started/index.rst
Normal file
93
docs/graphql/cloud/getting-started/index.rst
Normal file
@ -0,0 +1,93 @@
|
||||
.. meta::
|
||||
:description: Hasura Cloud getting started
|
||||
:keywords: hasura, docs, cloud, signup
|
||||
|
||||
.. _cloud_getting_started:
|
||||
|
||||
Getting Started with Hasura Cloud
|
||||
=================================
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Step 1: Create an account
|
||||
-------------------------
|
||||
|
||||
Navigate to `cloud.hasura.io
|
||||
<https://cloud.hasura.io/login>`__, and create a new Hasura Cloud account.
|
||||
|
||||
.. _cloud_connect_db:
|
||||
|
||||
Step 2: Connect new/existing database
|
||||
-------------------------------------
|
||||
|
||||
- To use an existing database, choose ``I have an existing Postgres database``.
|
||||
- To create a new database, choose ``Try a free database with Heroku``.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/getting-started/connect-db.png
|
||||
:alt: Connect new or existing database
|
||||
:width: 591px
|
||||
|
||||
Step 2a: Enter database URL (for existing database)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you chose ``I have an existing Postgres database`` in :ref:`Step 2 <cloud_connect_db>`, enter a database URL.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/getting-started/connect-existing-db.png
|
||||
:alt: Enter URL for existing database
|
||||
:width: 556px
|
||||
|
||||
Step 3: Create project
|
||||
----------------------
|
||||
|
||||
Click ``Create Project``.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/getting-started/create-project-new-db.png
|
||||
:alt: Create project for new database
|
||||
:width: 539px
|
||||
:group: create
|
||||
:class: inline-block
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/getting-started/create-project-existing-db.png
|
||||
:alt: Create project for existing database
|
||||
:width: 552px
|
||||
:group: create
|
||||
:class: inline-block
|
||||
|
||||
Next steps
|
||||
----------
|
||||
|
||||
Once you've created your project, you can get started with building with Hasura or manage your project.
|
||||
|
||||
.. contents::
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/getting-started/project-functionalities.png
|
||||
:alt: Project actions
|
||||
:width: 860px
|
||||
|
||||
|
||||
Explore the Hasura console
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Click ``Launch Console`` to open the Hasura console in your browser and :ref:`make your first GraphQL query <first_graphql_query>` or :ref:`set up your first event trigger <first_event_trigger>`.
|
||||
|
||||
You can navigate to the ``Pro`` tab to check out the Pro features that Hasura Cloud has set up for you.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/metrics/pro-tab-overview.png
|
||||
:alt: Hasura Console: Pro tab
|
||||
:width: 1118px
|
||||
|
||||
Manage your project
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Click the gear icon to :ref:`manage your project <manage_project>` (e.g. add collaborators, env vars or custom domains).
|
||||
|
||||
Add an admin secret
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
:ref:`Add an admin secret <secure_project>` to make sure that your GraphQL endpoint and the Hasura console are not publicly accessible.
|
66
docs/graphql/cloud/glossary.rst
Normal file
66
docs/graphql/cloud/glossary.rst
Normal file
@ -0,0 +1,66 @@
|
||||
.. meta::
|
||||
:description: Hasura Cloud glossary
|
||||
:keywords: hasura, docs, cloud, glossary
|
||||
|
||||
.. _glossary:
|
||||
|
||||
Glossary
|
||||
========
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Hasura Cloud Project
|
||||
--------------------
|
||||
|
||||
A Project is an individual GraphQL API hosted by Hasura Cloud. You
|
||||
can create a Project by going to the cloud dashboard and providing
|
||||
a PostgreSQL database URL. You can also provision a database on
|
||||
cloud platforms like Heroku from the Hasura Cloud Dashboard itself.
|
||||
|
||||
Each project is allocated a unique auto-generated name and an ID.
|
||||
You can use this name or ID while communicating to Hasura team
|
||||
regarding this project. Each project is also assigned a GraphQL API
|
||||
endpoint of the format ``https://<project-name>.hasura.app/v1/graphql``.
|
||||
|
||||
Editing the Project name and adding custom domains will be available soon.
|
||||
|
||||
For example, a project might be called ``usable-cobra-29`` with ID
|
||||
``bf0ea856-76a2-42c2-8a91-66ca9b9206e8``.
|
||||
|
||||
Hasura Cloud IP
|
||||
---------------
|
||||
|
||||
A Hasura Cloud IP will be listed on the Hasura Cloud Dashboard for
|
||||
each project. Hasura will be connecting to your database from this IP address.
|
||||
If your database is not exposed to the internet, you must allow connections
|
||||
from this IP address on your firewall settings
|
||||
for Hasura Cloud Project to function properly. Otherwise, Hasura will not
|
||||
be able to connect to your database and the GraphQL API will not be available.
|
||||
|
||||
Hasura Collaborator Token
|
||||
-------------------------
|
||||
|
||||
When you open the Hasura Console on a Cloud Project, you will not be asked to
|
||||
enter the admin secret like Hasura Core version. Instead, you will be
|
||||
automatically logged into the Console via an OAuth2.0 based authorization flow.
|
||||
You will be given the right access based on your permissions for the particular
|
||||
Hasura Cloud Project.
|
||||
|
||||
After the login process is complete, you'll see a new header called
|
||||
``Hasura-Collaborator-Token`` in the "Request Headers" section of GraphiQL.
|
||||
This token is used instead of admin secret to authenticate and authorize
|
||||
all the requests made from the Console. The token is only valid for 5mins
|
||||
and is refreshed silently by the Console. It is to be used only from Console.
|
||||
|
||||
For accessing the API from other clients, use the admin secret or create
|
||||
a Personal Access Token.
|
||||
|
||||
Hasura Client Name
|
||||
------------------
|
||||
|
||||
``Hasura-Client-Name`` will be set to ``hasura-console`` by default. It is
|
||||
used to identify the client who is making the request in Hasura Pro metrics
|
||||
and monitoring tools.
|
70
docs/graphql/cloud/hasurapro-cli/index.rst
Normal file
70
docs/graphql/cloud/hasurapro-cli/index.rst
Normal file
@ -0,0 +1,70 @@
|
||||
.. meta::
|
||||
:description: Hasura Pro CLI
|
||||
:keywords: hasura, docs, command line interface, cli
|
||||
|
||||
.. _hasurapro_cli:
|
||||
|
||||
Hasura Pro CLI
|
||||
==============
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
|
||||
Installing the Hasura Pro CLI
|
||||
-----------------------------
|
||||
|
||||
Hasura Pro CLI is distributed as a plugin to the :ref:`Hasura Core CLI <hasuracli_manual>`
|
||||
|
||||
- Follow the instructions :ref:`here <install_hasura_cli>` to install Hasura Core CLI
|
||||
|
||||
- Then execute the following command to install the Hasura Pro CLI plugin:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
hasura plugins install pro
|
||||
|
||||
- You can verify the installation by executing the ``help`` command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
hasura pro --help
|
||||
|
||||
Authentication with the Hasura Pro CLI
|
||||
--------------------------------------
|
||||
|
||||
All interactions from the CLI to Hasura’s APIs are authenticated using a personal access token generated for your user account.
|
||||
|
||||
To set up a token, execute the following command on the CLI:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
hasura pro login
|
||||
|
||||
This command will show a prompt for entering the personal access token.
|
||||
|
||||
Head to your `Hasura Cloud account settings <https://cloud.hasura.io>`_ to create a new token. You can name it something like "cli". Note that the token will be shown only once and as soon as you copy the token, paste it in your terminal prompt.
|
||||
|
||||
.. admonition:: Keep this token secure!
|
||||
|
||||
This token can be used to authenticate against Hasura Pro APIs and your Hasura Cloud projects. Make sure you keep it secure. This is a one-time operation. The token will be valid until you delete it.
|
||||
|
||||
Upgrading the CLI
|
||||
-----------------
|
||||
|
||||
To upgrade to a newer version, you can use the ``upgrade`` command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
hasura plugins upgrade pro
|
||||
|
||||
Uninstalling the CLI
|
||||
--------------------
|
||||
|
||||
To uninstall the CLI, use the ``uninstall`` command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
hasura plugins uninstall pro
|
74
docs/graphql/cloud/index.rst
Normal file
74
docs/graphql/cloud/index.rst
Normal file
@ -0,0 +1,74 @@
|
||||
.. meta::
|
||||
:description: Hasura Cloud documentation
|
||||
:keywords: hasura, docs, manual, graphql engine, cloud, hosted
|
||||
|
||||
.. title:: Hasura Cloud Documentation
|
||||
|
||||
.. _cloud_docs:
|
||||
|
||||
Hasura Cloud Documentation
|
||||
==========================
|
||||
|
||||
`Hasura Cloud <https://cloud.hasura.io/>`__ offers hosted `GraphQL Engine <https://github.com/hasura/graphql-engine>`__
|
||||
projects with extra features for reliability and security. It includes all the :ref:`core features <core_docs>`
|
||||
of GraphQL Engine, while taking care of infrastructure concerns, such as the number of instances, cores, memory, concurrent users, high-availability,
|
||||
realtime monitoring, caching, tracing, and rate-limiting. It supports both new and existing PostgreSQL databases.
|
||||
|
||||
.. container:: toc-list
|
||||
|
||||
.. container:: toc-list-section
|
||||
|
||||
.. container:: toc-list-head
|
||||
|
||||
Basics
|
||||
|
||||
.. container:: toc-list-content
|
||||
|
||||
- :ref:`cloud_getting_started`
|
||||
- :ref:`projects`
|
||||
|
||||
.. container:: toc-list-section
|
||||
|
||||
.. container:: toc-list-head
|
||||
|
||||
Features
|
||||
|
||||
.. container:: toc-list-content
|
||||
|
||||
- :ref:`metrics`
|
||||
- :ref:`api_limits`
|
||||
- :ref:`allow_lists`
|
||||
- :ref:`regression_tests`
|
||||
- :ref:`read_replicas`
|
||||
- :ref:`response_caching`
|
||||
- :ref:`tracing`
|
||||
|
||||
.. container:: toc-list-section
|
||||
|
||||
.. container:: toc-list-head
|
||||
|
||||
Reference
|
||||
|
||||
.. container:: toc-list-content
|
||||
|
||||
- :ref:`cloud_api_reference`
|
||||
- :ref:`glossary`
|
||||
- :ref:`hasurapro_cli`
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:titlesonly:
|
||||
:hidden:
|
||||
|
||||
Getting Started <getting-started/index>
|
||||
projects/index
|
||||
metrics/index
|
||||
api-limits
|
||||
allow-lists
|
||||
regression-tests
|
||||
read-replicas
|
||||
response-caching
|
||||
tracing
|
||||
hasurapro-cli/index
|
||||
api-reference
|
||||
glossary
|
23
docs/graphql/cloud/metrics/errors.rst
Normal file
23
docs/graphql/cloud/metrics/errors.rst
Normal file
@ -0,0 +1,23 @@
|
||||
.. meta::
|
||||
:description: Hasura Cloud error analysis
|
||||
:keywords: hasura, docs, cloud, reliability, errors
|
||||
|
||||
.. _errors:
|
||||
|
||||
Errors
|
||||
======
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Troubleshoot errors quickly with powerful analytical tools and filters:
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/metrics/pro-tab-errors.png
|
||||
:alt: Hasura Cloud Console errors tab
|
||||
|
||||
Drill into a specific operation via the magnifying-glass icon next to the operation summary in the 'Frequent errors' table. You'll be taken to a list of the failed operations, and can choose one to inspect the specific operation content, metadata, and error generated:
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/metrics/inspect-error.png
|
||||
:alt: Hasura Cloud Console inspect a failed operation
|
35
docs/graphql/cloud/metrics/index.rst
Normal file
35
docs/graphql/cloud/metrics/index.rst
Normal file
@ -0,0 +1,35 @@
|
||||
.. meta::
|
||||
:description: Hasura Cloud metrics
|
||||
:keywords: hasura, docs, cloud, metrics
|
||||
|
||||
.. _metrics:
|
||||
|
||||
Metrics
|
||||
=======
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Hasura Cloud projects include Pro features for enhanced reliability and performance
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/metrics/pro-tab-overview.png
|
||||
:alt: Hasura Cloud Console overview tab
|
||||
|
||||
Learn more
|
||||
----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:titlesonly:
|
||||
|
||||
overview
|
||||
errors
|
||||
usage
|
||||
operations
|
||||
websockets
|
||||
subscription-workers
|
119
docs/graphql/cloud/metrics/operations.rst
Normal file
119
docs/graphql/cloud/metrics/operations.rst
Normal file
@ -0,0 +1,119 @@
|
||||
.. meta::
|
||||
:description: Hasura Cloud operation analysis
|
||||
:keywords: hasura, docs, cloud, reliability, operations
|
||||
|
||||
.. _operations:
|
||||
|
||||
Operations
|
||||
==========
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Terminology
|
||||
-----------
|
||||
|
||||
**Operation**
|
||||
Any GraphQL (query, mutation, subscription) request made to v1/graphql endpoint of a GraphQL engine instance
|
||||
|
||||
**Operation name**
|
||||
(Optional) Any GraphQL request can be labelled with a name by the client making the query. In this example, *getPollOptions* is the operation name:
|
||||
|
||||
.. code-block:: graphql
|
||||
|
||||
query getPollOptions (
|
||||
poll {
|
||||
options {
|
||||
id
|
||||
name
|
||||
description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
**Operation ID**
|
||||
A unique, auto-generated hash for each Operation name; can distinguish between different operations executed with the same name
|
||||
|
||||
**Operation type**
|
||||
Whether the operation is a query, mutation, or subscription
|
||||
|
||||
**Request ID**
|
||||
A unique, auto-generated ID for each request, comes from x-request-id HTTP header
|
||||
|
||||
**Websocket ID**
|
||||
A unique ID generated by the server when a websocket connection is established by the client
|
||||
|
||||
**Websocket Operation ID**
|
||||
A unique ID generated by the websocket client for each operation it is sending to the server
|
||||
|
||||
Operations
|
||||
----------
|
||||
|
||||
Processed realtime logs of all requests to the GraphQL Engine. View all and inspect individual operations:
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/metrics/pro-tab-operations.png
|
||||
:alt: Hasura Cloud Console list operations
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/metrics/pro-tab-operations-inspect.png
|
||||
:alt: Hasura Cloud Console inspect operation
|
||||
|
||||
Filtering operations
|
||||
--------------------
|
||||
|
||||
Click on the **Filter** header to open the menu of filter options
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Filter option
|
||||
- Filter type
|
||||
- Example or options
|
||||
* - Time range
|
||||
- timestamps
|
||||
- last hour, last 6 hours, last 12 hours, last 24 hours, or custom range
|
||||
* - Operation ID
|
||||
- string (text field)
|
||||
- 5284946f4e15aa81bc868316d56aa68f
|
||||
* - Operation Name
|
||||
- string (text field)
|
||||
- getPollOptions
|
||||
* - Request ID
|
||||
- string (text field)
|
||||
- e15f7bj9-3b9f-4152-92a4-e745471514af
|
||||
* - Show only errors
|
||||
- boolean (checkbox field)
|
||||
- true or false (default false)
|
||||
* - Websocket ID
|
||||
- string (text field)
|
||||
- def703fc-851f-48e8-8e5c-cbdead37b2fe
|
||||
* - Websocket Operation ID
|
||||
- string (text field)
|
||||
- 110
|
||||
* - Hide introspection query
|
||||
- boolean (checkbox field)
|
||||
- true or false (default false)
|
||||
* - Operation Type
|
||||
- enum (select field)
|
||||
- query, mutation, subscription, all
|
||||
* - Role
|
||||
- enum (select field)
|
||||
- from *x-hasura-user-role*: no role, admin, user, ...
|
||||
* - Error Code
|
||||
- enum (select field)
|
||||
- no error code, access-denied, depth-limit-exceeded, ...
|
||||
* - Client Name
|
||||
- enum (select field)
|
||||
- no client name, hasura-console, hasura-test-runner, ...
|
||||
* - Transport
|
||||
- enum (select field)
|
||||
- http or ws
|
||||
* - Status
|
||||
- enum (select field)
|
||||
- started or closed
|
||||
|
||||
Sorting operations
|
||||
------------------
|
||||
|
||||
Each column in the **Operations List** can be sorted (ascending or descending) just by clicking on it. Very useful for identifying operations with unusually high execution time or response size!
|
18
docs/graphql/cloud/metrics/overview.rst
Normal file
18
docs/graphql/cloud/metrics/overview.rst
Normal file
@ -0,0 +1,18 @@
|
||||
.. meta::
|
||||
:description: Hasura Cloud overview
|
||||
:keywords: hasura, docs, cloud, overview
|
||||
|
||||
.. _overview:
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
The Overview displays aggregate stats over the last hour:
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/metrics/pro-tab-overview.png
|
||||
:alt: Hasura Cloud Console overview tab
|
18
docs/graphql/cloud/metrics/subscription-workers.rst
Normal file
18
docs/graphql/cloud/metrics/subscription-workers.rst
Normal file
@ -0,0 +1,18 @@
|
||||
.. meta::
|
||||
:description: Hasura Cloud subscription workers
|
||||
:keywords: hasura, docs, cloud, reliability, subscriptions
|
||||
|
||||
.. _subscription_workers:
|
||||
|
||||
Subscription workers
|
||||
====================
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Subscription workers offers a subscription-worker-specific version of usage statistics:
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/metrics/pro-tab-subscription-workers.png
|
||||
:alt: Hasura Cloud Console subscription workers tab
|
23
docs/graphql/cloud/metrics/usage.rst
Normal file
23
docs/graphql/cloud/metrics/usage.rst
Normal file
@ -0,0 +1,23 @@
|
||||
.. meta::
|
||||
:description: Hasura Cloud usage analysis
|
||||
:keywords: hasura, docs, cloud, reliability, usage
|
||||
|
||||
.. _usage:
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Learn about usage with aggregate summaries and filtering tools:
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/metrics/pro-tab-usage.png
|
||||
:alt: Hasura Cloud Console usage tab
|
||||
|
||||
Drill into an operation via its magnifying-glass icon in the 'Query List' table. You'll be taken to a list of similar operations, and can choose one to inspect for content, metadata, and error generated:
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/metrics/pro-tab-operations-inspect.png
|
||||
:alt: Hasura Cloud Console inspect an operation
|
18
docs/graphql/cloud/metrics/websockets.rst
Normal file
18
docs/graphql/cloud/metrics/websockets.rst
Normal file
@ -0,0 +1,18 @@
|
||||
.. meta::
|
||||
:description: Hasura Cloud websockets analysis
|
||||
:keywords: hasura, docs, cloud, reliability, websockets
|
||||
|
||||
.. _websockets:
|
||||
|
||||
Websockets
|
||||
==========
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Websockets is a ws-specific version of usage statistics:
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/metrics/pro-tab-websockets.png
|
||||
:alt: Hasura Cloud Console websockets tab
|
89
docs/graphql/cloud/projects/create.rst
Normal file
89
docs/graphql/cloud/projects/create.rst
Normal file
@ -0,0 +1,89 @@
|
||||
.. meta::
|
||||
:description: Creating projects on Hasura Cloud
|
||||
:keywords: hasura, cloud, docs, start
|
||||
|
||||
.. _create_project:
|
||||
|
||||
Creating projects
|
||||
=================
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 2
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
You can create a new Hasura Cloud project with either a new Postgres database, or an existing Postgres database with a publicly available IP address.
|
||||
|
||||
To begin, navigate to the ``Projects`` page, and click the ``New Project`` link.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/create-new-project.png
|
||||
:alt: create new project button
|
||||
:width: 400
|
||||
|
||||
Creating a project with a new database
|
||||
--------------------------------------
|
||||
|
||||
Hasura Cloud does not host databases, but does provide integrations with which you can create databases on managed cloud providers like Heroku. Integrations for AWS, GCP, and Azure are coming soon.
|
||||
|
||||
To get started, click ``Try with Heroku``, and follow the prompts to authenticate with Heroku. Hasura Cloud will integrate with your Heroku account and manage the initial setup of a dev-tier Postgres instance. You can always upgrade the instance and manage options later through your Heroku account.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/create-project-heroku.png
|
||||
:alt: create new hosted project
|
||||
|
||||
Creating a project with an existing database
|
||||
--------------------------------------------
|
||||
|
||||
To create a new Hasura Cloud project connected to an existing Postgres database, click ``Enter Database URL``, and enter your database connection string (looks like ``postgres://username:password@hostname:port/dbname``).
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/new-project-hosted.png
|
||||
:alt: create new hosted project
|
||||
|
||||
Allowing connections from Hasura Cloud IP
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For some cloud services, like GCP, you'll need to adjust your Postgres connection settings to allow connections from the Hasura Cloud IP address. You can copy the IP address from either the copy icon in the ``Database Setup`` window, or the ``Hasura Cloud IP`` field on the project's details view. You may also need to disable SSL. Adding custom cert information to a Hasura Cloud instance is not yet available.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/existing-db-setup.png
|
||||
:alt: Existing database setup
|
||||
:width: 568px
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/hasura-cloud-ip.png
|
||||
:alt: Hasura Cloud IP field
|
||||
:width: 1200px
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/gcp-postgres-authorized-network.png
|
||||
:alt: whitelist Hasura instance IP in Postgres settings
|
||||
:width: 727px
|
||||
|
||||
Connecting to a database not exposed over the internet
|
||||
------------------------------------------------------
|
||||
|
||||
`Contact us <https://hasura.io/contact-us/>`__ for VPC peering and on-premise solutions.
|
||||
|
||||
.. _cloud_postgres_permissions:
|
||||
|
||||
Postgres permissions
|
||||
--------------------
|
||||
|
||||
Hasura Cloud works with **Postgres versions 9.5 and above**.
|
||||
|
||||
If you’re running in a controlled environment, you might need to configure
|
||||
Hasura Cloud to use a specific Postgres user that your DBA gives you.
|
||||
|
||||
Apart from the :ref:`Hasura Core Postgres permissions <postgres_permissions>`,
|
||||
Hasura Cloud needs the following extra permissions:
|
||||
|
||||
- (required) Read and write access to ``hdb_pro_catalog`` schema.
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
-- execute these statements after executing the ones mentioned in Hasura Core docs
|
||||
-- create the schemas required by the hasura cloud system
|
||||
CREATE SCHEMA IF NOT EXISTS hdb_pro_catalog;
|
||||
|
||||
-- make the user an owner of system schemas
|
||||
ALTER SCHEMA hdb_pro_catalog OWNER TO hasurauser;
|
||||
|
19
docs/graphql/cloud/projects/delete.rst
Normal file
19
docs/graphql/cloud/projects/delete.rst
Normal file
@ -0,0 +1,19 @@
|
||||
.. meta::
|
||||
:description: Deleting projects on Hasura Cloud
|
||||
:keywords: hasura, docs, delete
|
||||
|
||||
.. _delete_project:
|
||||
|
||||
Deleting projects
|
||||
=================
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
You can delete a project with the ``Delete Project`` button at the bottom of the project's details view.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/projects-delete.png
|
||||
:alt: delete a project
|
||||
:width: 1199px
|
52
docs/graphql/cloud/projects/index.rst
Normal file
52
docs/graphql/cloud/projects/index.rst
Normal file
@ -0,0 +1,52 @@
|
||||
.. meta::
|
||||
:description: Managing teams in Hasura Cloud
|
||||
:keywords: hasura, docs, cloud, teams
|
||||
|
||||
.. _projects:
|
||||
|
||||
Projects & teams
|
||||
================
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The ``Projects`` page show a list of your projects.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/projects-list.png
|
||||
:alt: Projects list
|
||||
:width: 1200px
|
||||
|
||||
For each project, you can:
|
||||
|
||||
- Click the gear icon to :ref:`manage your project <manage_project>`, or
|
||||
- Click ``Launch Console`` to open the Hasura console in your browser, and navigate to the ``Pro`` tab to check out the Pro features that Hasura Cloud has set up for you.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/getting-started/project-actions.png
|
||||
:alt: Project actions
|
||||
:width: 860px
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/metrics/pro-tab-overview.png
|
||||
:alt: Hasura Console: Pro tab
|
||||
:width: 1118px
|
||||
|
||||
.. note::
|
||||
|
||||
Please see the :ref:`API reference <cloud_api_reference>` to create and manage Hasura Cloud projects programmatically.
|
||||
|
||||
Dig deeper
|
||||
----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:titlesonly:
|
||||
|
||||
Creating projects <create>
|
||||
Managing projects <manage-projects/index>
|
||||
Securing projects <secure>
|
||||
Deleting projects <delete>
|
||||
|
71
docs/graphql/cloud/projects/manage-projects/domains.rst
Normal file
71
docs/graphql/cloud/projects/manage-projects/domains.rst
Normal file
@ -0,0 +1,71 @@
|
||||
.. meta::
|
||||
:description: Managing domains on Hasura Cloud
|
||||
:keywords: hasura, docs, project, domains
|
||||
|
||||
.. _manage_project_domains:
|
||||
|
||||
Domains tab
|
||||
===========
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 2
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
In the ``Domains`` tab, you can see the default Hasura domain, and you have the possibility to add custom domains.
|
||||
|
||||
Adding a custom domain
|
||||
----------------------
|
||||
|
||||
You can add a custom domain to your Hasura Cloud project by following the steps below.
|
||||
|
||||
Step 1: Navigate to add a custom domain
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
On the ``Domains`` tab, click on the ``New Custom Domain`` button.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/add-custom-domain.png
|
||||
:alt: Add custom domain
|
||||
:width: 727px
|
||||
|
||||
Step 2: Add your custom domain
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Enter your custom domain and click the ``Add`` button.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/choose-custom-domain.png
|
||||
:alt: Choose custom domain
|
||||
:width: 727px
|
||||
|
||||
Step 3: Add the record to your DNS
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
After adding a custom domain, the following window will show up:
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/dns-settings.png
|
||||
:alt: DNS settings
|
||||
:width: 727px
|
||||
|
||||
If you haven't already done so, add the default Hasura domain as a ``CNAME`` record to your DNS.
|
||||
|
||||
Until this is done, the dashboard will show a notice that the DNS validation is pending.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/dns-validation-pending.png
|
||||
:alt: DNS validation pending
|
||||
:width: 727px
|
||||
|
||||
.. note::
|
||||
|
||||
Depending on your DNS provider, it might take up to 24 hours for the DNS record to be added.
|
||||
|
||||
DNS validated
|
||||
-------------
|
||||
|
||||
Once the DNS is validated, the dashboard will update the status with the following notice:
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/dns-validated.png
|
||||
:alt: DNS validated
|
||||
:width: 727px
|
27
docs/graphql/cloud/projects/manage-projects/env-vars.rst
Normal file
27
docs/graphql/cloud/projects/manage-projects/env-vars.rst
Normal file
@ -0,0 +1,27 @@
|
||||
.. meta::
|
||||
:description: Managing env vars on Hasura Cloud
|
||||
:keywords: hasura, docs, project, env vars
|
||||
|
||||
.. _manage_project_env_vars:
|
||||
|
||||
Env vars tab
|
||||
============
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 2
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The ``Env vars`` tab allows setting :ref:`Hasura GraphQL Engine env variables <command-flags>` and adding other custom env variables as well.
|
||||
|
||||
Adding an env var
|
||||
-----------------
|
||||
|
||||
Click on the ``New Env Var`` button and either choose an env var from the dropdown or add a custom env var.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/add-env-var.png
|
||||
:alt: add env var options
|
||||
:width: 1200px
|
31
docs/graphql/cloud/projects/manage-projects/general.rst
Normal file
31
docs/graphql/cloud/projects/manage-projects/general.rst
Normal file
@ -0,0 +1,31 @@
|
||||
.. meta::
|
||||
:description: Managing projects on Hasura Cloud
|
||||
:keywords: hasura, docs, project, general
|
||||
|
||||
.. _manage_project_general:
|
||||
|
||||
General tab
|
||||
===========
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 2
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The ``General`` tab shows the project's general settings.
|
||||
|
||||
General settings
|
||||
----------------
|
||||
|
||||
- **Name**: Unique auto-generated name for the project
|
||||
- **ID**: Unique auto-generated ID for the project
|
||||
- **GraphQL API**: GraphQL endpoint for the project
|
||||
- **Admin Secret**: Secret for securing the GraphQL endpoint
|
||||
- **Owner**: Email of the project owner
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/project-details.png
|
||||
:alt: General tab
|
||||
:width: 1163px
|
36
docs/graphql/cloud/projects/manage-projects/index.rst
Normal file
36
docs/graphql/cloud/projects/manage-projects/index.rst
Normal file
@ -0,0 +1,36 @@
|
||||
.. meta::
|
||||
:description: Managing projects on Hasura Cloud
|
||||
:keywords: hasura, docs, project
|
||||
|
||||
.. _manage_project:
|
||||
|
||||
Managing projects
|
||||
=================
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 2
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
To view a project's details, and manage its teams and environment variables, navigate to the ``Projects`` page and click the gear icon on the project.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/manage-project.png
|
||||
:alt: Manage project
|
||||
:width: 865px
|
||||
|
||||
Managing projects
|
||||
-----------------
|
||||
|
||||
See the below pages for detailed guides on managing projects:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:titlesonly:
|
||||
|
||||
General tab <general>
|
||||
Team tab <team>
|
||||
Env vars tab <env-vars>
|
||||
Domains tab <domains>
|
46
docs/graphql/cloud/projects/manage-projects/team.rst
Normal file
46
docs/graphql/cloud/projects/manage-projects/team.rst
Normal file
@ -0,0 +1,46 @@
|
||||
.. meta::
|
||||
:description: Managing teams on Hasura Cloud
|
||||
:keywords: hasura, docs, project, team
|
||||
|
||||
.. _manage_project_team:
|
||||
|
||||
Team tab
|
||||
========
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 2
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The ``Team`` tab shows the current people with access to the project.
|
||||
|
||||
Add a collaborator
|
||||
------------------
|
||||
|
||||
Click ``New Collaborator`` to invite a new team member.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/team-view.png
|
||||
:alt: Team tab
|
||||
:width: 1146px
|
||||
|
||||
Team roles
|
||||
^^^^^^^^^^
|
||||
|
||||
Team members can have different levels of access in the Hasura console.
|
||||
|
||||
- **Admin** has complete access to all project tools and configurations.
|
||||
- **User** has limited privileges:
|
||||
|
||||
- The ``Execute GraphQL`` permission allows running queries, mutations, and subscriptions.
|
||||
- The ``View Metrics`` permission allows inspecting operation data and the performance dashboard.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/add-collaborator.png
|
||||
:alt: Add collaborator
|
||||
:width: 437px
|
||||
|
||||
.. note::
|
||||
|
||||
Invitations can be accepted or declined via the ``Invitations`` tab of your Hasura Cloud settings.
|
59
docs/graphql/cloud/projects/secure.rst
Normal file
59
docs/graphql/cloud/projects/secure.rst
Normal file
@ -0,0 +1,59 @@
|
||||
.. meta::
|
||||
:description: Securing projects on Hasura Cloud
|
||||
:keywords: hasura, docs, project
|
||||
|
||||
.. _secure_project:
|
||||
|
||||
Securing projects
|
||||
=================
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 2
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
To make sure that your GraphQL endpoint and the Hasura console are not publicly accessible, you need to configure an admin secret key.
|
||||
|
||||
Adding an admin secret
|
||||
----------------------
|
||||
|
||||
Step 1: Go to settings
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
On the project overview, click on the settings icon on the top right of the relevant project.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/secure-settings.png
|
||||
:alt: Go to settings
|
||||
:width: 865px
|
||||
|
||||
Step 2: Navigate to env vars
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
On the ``Env vars`` tab, click the button to add a new env var.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/secure-envvars.png
|
||||
:alt: Navigate to env vars
|
||||
:width: 865px
|
||||
|
||||
Step 3: Add an admin secret
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In the ``Key`` dropdown, choose ``ADMIN_SECRET`` and add the secret in the ``Value`` field. Then click the ``Add`` button.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/projects/secure-add-envvar.png
|
||||
:alt: Navigate to env vars
|
||||
:width: 865px
|
||||
|
||||
Accessing Hasura
|
||||
----------------
|
||||
|
||||
After setting an admin secret, when you launch the console from the Hasura Cloud dashboard, you'll be authenticated as an admin.
|
||||
If you want to make API calls from outside the console, you need to pass the admin secret as the `x-hasura-admin-secret` request header.
|
||||
|
||||
.. note::
|
||||
|
||||
The admin secret should be treated like a password i.e. it should be kept secret and shouldn't be passed from frontend clients.
|
||||
Refer :ref:`this <authentication>` to set up user authentication.
|
42
docs/graphql/cloud/read-replicas.rst
Normal file
42
docs/graphql/cloud/read-replicas.rst
Normal file
@ -0,0 +1,42 @@
|
||||
.. meta::
|
||||
:description: Hasura Cloud read replicas
|
||||
:keywords: hasura, docs, cloud, read replicas, connections, pool
|
||||
|
||||
.. _read_replicas:
|
||||
|
||||
Read replicas
|
||||
=============
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Hasura Cloud can load balance queries and subscriptions across read replicas while sending all mutations and metadata API calls to the master.
|
||||
|
||||
Adding read replica urls
|
||||
------------------------
|
||||
|
||||
If you have configured your Postgres instances with replicas, the replica URLs can be added to Hasura using the following environment variable in your project ENV Vars tab:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
HASURA_GRAPHQL_READ_REPLICA_URLS=postgres://user:password@replica-host:5432/db
|
||||
|
||||
If you have multiple replicas, their urls can be added as comma separated values.
|
||||
|
||||
Connection pool parameters
|
||||
--------------------------
|
||||
|
||||
Additional environment variables for connection pools, and for read replicas specifically:
|
||||
|
||||
``HASURA_GRAPHQL_PG_STRIPES``
|
||||
|
||||
``HASURA_GRAPHQL_PG_CONNECTIONS``
|
||||
|
||||
``HASURA_GRAPHQL_CONNECTIONS_PER_READ_REPLICA``
|
||||
|
||||
``HASURA_GRAPHQL_STRIPES_PER_READ_REPLICA``
|
97
docs/graphql/cloud/regression-tests.rst
Normal file
97
docs/graphql/cloud/regression-tests.rst
Normal file
@ -0,0 +1,97 @@
|
||||
.. meta::
|
||||
:description: Hasura Cloud regression tests
|
||||
:keywords: hasura, docs, cloud, reliability, regression, migration
|
||||
|
||||
.. _regression_tests:
|
||||
|
||||
Regression tests
|
||||
================
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 2
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Hasura Cloud includes a comprehensive test bench that lets you seamlessly compile a test suite on each project, executable on any GraphQL Engine instance (ex: staging, prod).
|
||||
|
||||
Here's a reference development workflow that is enabled by Hasura Cloud:
|
||||
|
||||
#. Build your database schema and configure Hasura as required by your frontend apps or public GraphQL API.
|
||||
#. Deploy changes to production after testing them.
|
||||
#. Create a regression suite on production.
|
||||
#. Iterate on your GraphQL schema to support new features or edits.
|
||||
|
||||
- Test changes in your dev instance against the production instance’s regression test suite. Fix any issues highlighted by the tests or plan to communicate regressions to affected stakeholders.
|
||||
|
||||
#. Run all changes through a CI/CD pipeline
|
||||
|
||||
- Run Regression tests programmatically for all changes in the team
|
||||
|
||||
#. Promote changes to prod
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/reliability/regression-testing-diagram.png
|
||||
:alt: Regression testing process diagram
|
||||
|
||||
Manage test suites
|
||||
------------------
|
||||
|
||||
Each Hasura Cloud project can be configured with a separate test suite. Ideally, you want to create a regression test suite on an project which has received requests with operations you’d like to continue supporting or ensure are not “broken” - production or a shared QA project which receives operations in your app or, if you have a public GraphQL API, those from your consumers.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/reliability/regression-tests-suites.png
|
||||
:alt: Manage regression test suites
|
||||
|
||||
Quick-create tests
|
||||
------------------
|
||||
|
||||
Add important operations to your test suite with one click by adding them from your project's operation history:
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/reliability/regression-tests-add-operations.png
|
||||
:alt: Add tests to regression test suites
|
||||
|
||||
Run test suites
|
||||
---------------
|
||||
|
||||
A good development workflow would require that tests be run 1) early in the dev process, and 2) automatically with changes, to ensure changes to the schema don't break functionality.
|
||||
|
||||
A test suite configured on a Hasura Cloud project can be run on the same instance or any other Hasura Cloud project registered to your team, including local ones. This is how we recommend that you incorporate regression tests into your GraphQL engine workflows:
|
||||
|
||||
Run regression tests manually
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Let’s say you’re a developer iterating on a feature and, as part of your work, need to modify your Postgres schema or the Hasura configuration. It is likely that you are doing so by running the console via the Hasura CLI to generate migrations that you can version control. Before committing your changes in git, you should run tests to get an early warning for potential regressions. Your team may want to designate the test suite from your production instance (or a suitable alternative) as the default suite to be used for this, and you can choose to run this test suite on your local or development instance.
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/reliability/regressions-run-prod-tests-on-dev.png
|
||||
:alt: Run regression tests
|
||||
|
||||
For example, if the column ‘title’ (in a typical authors and articles schema) has been modified as part of a feature iteration. Assuming the operation from the previous example is part of the test suite on production, here’s how the feedback on this change looks like:
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/reliability/regression-tests-results.png
|
||||
:alt: Regression test results
|
||||
|
||||
As you can see, one of the tests fails because it expects a field, title, to be part of the type articles - which is something our proposed change just modified and removed support for.
|
||||
|
||||
Run regression tests in CI/CD flow
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/reliability/regression-tests-run-cli.png
|
||||
:alt: Run regression tests via CLI
|
||||
|
||||
This command will fetch the entire test suite from Hasura Pro and run the tests against given endpoint using the admin secret and report the result on the terminal. The test run and the results will also be available on the Hasura Console.
|
||||
|
||||
You can use the Hasura Pro CLI to programmatically trigger execution of a test suite in your automated testing setup, typically in CI scripts.
|
||||
|
||||
In order to communicate with Hasura’s APIs, the CLI needs to be configured with an API access token (which you can create via your Hasura Cloud settings). If you want to set the token up on a non-interactive environment, like a CI pipeline, you can obtain a token and then add to ``~/.hasura/pro_config.yaml`` with the following format:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pat: <token>
|
||||
|
||||
|
||||
View test suite results
|
||||
-----------------------
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/reliability/regression-tests-past-runs.png
|
||||
:alt: Regression tests past results
|
66
docs/graphql/cloud/response-caching.rst
Normal file
66
docs/graphql/cloud/response-caching.rst
Normal file
@ -0,0 +1,66 @@
|
||||
.. meta::
|
||||
:description: Query response caching in Hasura Cloud
|
||||
:keywords: hasura, docs, cloud, response, caching
|
||||
|
||||
.. _response_caching:
|
||||
|
||||
Query response caching
|
||||
======================
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Hasura Cloud provides support for caching query responses, in order to
|
||||
improve performance for queries which are executed frequently.
|
||||
|
||||
Cached responses are stored in for a period of time in a LRU (least-recently
|
||||
used) cache, and removed from the cache as needed based on usage.
|
||||
|
||||
A query's response can be cached only if the following conditions hold:
|
||||
|
||||
- The query does not make use of remote schemas or remote joins
|
||||
- The query and any related user permissions do not make use of session variables
|
||||
- The response JSON is under 100KB in size
|
||||
|
||||
Enable caching
|
||||
--------------
|
||||
|
||||
In order to enable caching for a query response, or to return an existing
|
||||
response from the cache (if one exists), simply add the ``@cached`` directive
|
||||
to your query:
|
||||
|
||||
.. code-block:: graphql
|
||||
|
||||
query MyCachedQuery @cached {
|
||||
users {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
|
||||
If the response was cached successfully, the HTTP response will include a
|
||||
``X-Hasura-TTL`` header, whose value indicates the maximum number of seconds
|
||||
for the returned response to remain in the cache.
|
||||
|
||||
Controlling cache lifetime
|
||||
--------------------------
|
||||
|
||||
The maximum lifetime of an entry in the cache can be controlled using the ``ttl``
|
||||
argument to the ``@cached`` query directive. The value is an integer number of seconds:
|
||||
|
||||
.. code-block:: graphql
|
||||
|
||||
query MyCachedQuery @cached(ttl: 120) {
|
||||
users {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
|
||||
By default, a response will be cached with a maximum lifetime of 60 seconds.
|
||||
The maximum allowed value is 300 seconds (5 minutes).
|
98
docs/graphql/cloud/tracing.rst
Normal file
98
docs/graphql/cloud/tracing.rst
Normal file
@ -0,0 +1,98 @@
|
||||
.. meta::
|
||||
:description: Distributed tracing with Hasura Cloud
|
||||
:keywords: hasura, docs, cloud, tracing
|
||||
|
||||
.. _tracing:
|
||||
|
||||
Distributed tracing
|
||||
=======================
|
||||
|
||||
.. contents:: Table of contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Hasura Cloud has support for *distributed tracing*, a technique for
|
||||
debugging Hasura in production in the context of the services it works
|
||||
with. These services might include your own Postgres database, any
|
||||
remote schemas, event trigger webhook providers, action providers or
|
||||
authentication hooks. Distributed tracing attempts to give a unified
|
||||
view into the performance characteristics of all of these components of
|
||||
your architecture.
|
||||
|
||||
Visualizing traces
|
||||
------------------
|
||||
|
||||
The Hasura Pro console makes it possible to view Hasura's own tracing
|
||||
data, by opening the details view for an operation in the Operations
|
||||
tab:
|
||||
|
||||
.. thumbnail:: /img/graphql/cloud/tracing/tracing-operations-timing.png
|
||||
:alt: View timing data in the Operations tab
|
||||
|
||||
Given that other system components will report their own tracing data to
|
||||
your APM system, and not to Hasura, it is not possible to give a
|
||||
complete picture of a trace, but since Hasura sits in a central position
|
||||
in the architecture of many systems, it can often give a reasonably
|
||||
comprehensive view of the provenance of data in your system.
|
||||
|
||||
For example, Hasura can report interactions with Postgres, remote
|
||||
schemas, event trigger webhooks and action handlers.
|
||||
|
||||
APM system integration
|
||||
----------------------
|
||||
|
||||
Hasura will report trace information to your APM or *application
|
||||
performance monitoring* system, where it can be correlated with similar
|
||||
sources of data from other components of your service architecture.
|
||||
|
||||
If you are considering integrating Hasura with your APM system, please
|
||||
get in touch so that we can help to coordinate that effort.
|
||||
|
||||
Trace propagation
|
||||
-----------------
|
||||
|
||||
At the boundaries between different services, tracing information needs
|
||||
to be shared in order for trace fragments from different systems to be
|
||||
correlated with each other in the APM system. This is called *trace
|
||||
propagation*.
|
||||
|
||||
There are several subtly-incompatible proposals for trace propagation,
|
||||
which can make it difficult to arrange for any two services to work
|
||||
together.
|
||||
|
||||
Propagation to web services
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For propagation during a call to a web service over HTTP, Hasura
|
||||
currently implements the `B3 propagation
|
||||
specification <https://github.com/openzipkin/b3-propagation>`__. This
|
||||
means that we send trace information in various HTTP headers, which
|
||||
should be read and handled by any compatible web services.
|
||||
|
||||
If you are unsure how to implement B3 propagation in your own web
|
||||
service, the simplest thing to do is to read these headers and pass them
|
||||
along to any HTTP services you call which also support B3 propagation,
|
||||
including Hasura itself.
|
||||
|
||||
In particular, if an event trigger webhook or action handler propagates
|
||||
these B3 headers back to Hasura, we will be able to trace the entire
|
||||
interaction.
|
||||
|
||||
Propagation via Postgres
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There is no standard method for trace propagation via Postgres
|
||||
transactions. For example, event triggers can be invoked by mutations,
|
||||
and so their traces should be correlated.
|
||||
|
||||
For this reason, we have adopted our own method of propagating a trace
|
||||
context in Postgres transactions.
|
||||
|
||||
The trace context will be serialized during mutations as a
|
||||
transaction-local variable, ``hasura.tracecontext``. This value has the
|
||||
Postgres type ``json``, and it can be read in trigger functions and
|
||||
propagated to any downstream services.
|
@ -34,14 +34,14 @@ This page provides reference examples of typical action use cases.
|
||||
|
||||
Now we have an action ``updateAddress`` that looks as follows:
|
||||
|
||||
.. thumbnail:: /img/graphql/manual/actions/update-address-action-definition.png
|
||||
.. thumbnail:: /img/graphql/core/actions/update-address-action-definition.png
|
||||
:alt: Update address action
|
||||
:width: 65%
|
||||
|
||||
We can now add an object relationship from the ``updateAddress`` action to the ``authors`` table in our schema.
|
||||
We call it ``updatedAddressAuthor``.
|
||||
|
||||
.. thumbnail:: /img/graphql/manual/actions/action-object-relationship.png
|
||||
.. thumbnail:: /img/graphql/core/actions/action-object-relationship.png
|
||||
:alt: Object relationship from action
|
||||
:width: 65%
|
||||
|
||||
@ -90,14 +90,14 @@ This page provides reference examples of typical action use cases.
|
||||
|
||||
Now we have an action ``updateAuthor`` that looks as follows:
|
||||
|
||||
.. thumbnail:: /img/graphql/manual/actions/update-author-action-definition.png
|
||||
.. thumbnail:: /img/graphql/core/actions/update-author-action-definition.png
|
||||
:alt: Update author action
|
||||
:width: 65%
|
||||
|
||||
We can now add an array relationship from the ``updateAuthor`` action to the ``articles`` table in our schema.
|
||||
We call it ``updatedAuthorArticles``.
|
||||
|
||||
.. thumbnail:: /img/graphql/manual/actions/action-array-relationship.png
|
||||
.. thumbnail:: /img/graphql/core/actions/action-array-relationship.png
|
||||
:alt: Array relationship from action
|
||||
:width: 65%
|
||||
|
@ -183,7 +183,7 @@ For your action, add a header that will act as an action secret.
|
||||
Head to the ``Actions -> [action-name]`` tab in the console and scroll down to ``Headers``.
|
||||
You can now configure an action secret by adding a header:
|
||||
|
||||
.. thumbnail:: /img/graphql/manual/actions/action-secret-header.png
|
||||
.. thumbnail:: /img/graphql/core/actions/action-secret-header.png
|
||||
:alt: Console action secret
|
||||
:width: 75%
|
||||
|
@ -29,7 +29,7 @@ Set action permissions
|
||||
Head to the ``Actions -> [action-name] -> Permissions`` tab in the
|
||||
console.
|
||||
|
||||
.. thumbnail:: /img/graphql/manual/actions/actions-permissions.png
|
||||
.. thumbnail:: /img/graphql/core/actions/actions-permissions.png
|
||||
:alt: Console action permission
|
||||
|
||||
Hit ``Save`` to give the role permission to access the action.
|
@ -41,7 +41,7 @@ Generating handler code for your action
|
||||
You can select the framework of your choice to get the corresponding
|
||||
handler boilerplate code.
|
||||
|
||||
.. thumbnail:: /img/graphql/manual/actions/console-codegen-tab.png
|
||||
.. thumbnail:: /img/graphql/core/actions/console-codegen-tab.png
|
||||
:alt: Console codegen tab
|
||||
|
||||
|
||||
@ -59,17 +59,17 @@ Generating handler code for your action
|
||||
|
||||
1. Choose which framework you want to codegen for:
|
||||
|
||||
.. thumbnail:: /img/graphql/manual/actions/cli-framework-prompt.png
|
||||
.. thumbnail:: /img/graphql/core/actions/cli-framework-prompt.png
|
||||
:alt: CLI Framework Prompt
|
||||
|
||||
2. Choose if you also wish to clone a starter kit for the chosen framework:
|
||||
|
||||
.. thumbnail:: /img/graphql/manual/actions/cli-starter-kit-prompt.png
|
||||
.. thumbnail:: /img/graphql/core/actions/cli-starter-kit-prompt.png
|
||||
:alt: CLI Starter Kit Prompt
|
||||
|
||||
3. Choose a path where you want to output the auto-generated code files
|
||||
|
||||
.. thumbnail:: /img/graphql/manual/actions/cli-output-dir-prompt.png
|
||||
.. thumbnail:: /img/graphql/core/actions/cli-output-dir-prompt.png
|
||||
:alt: CLI Starter Kit Prompt
|
||||
|
||||
|
@ -96,7 +96,7 @@ in the GraphQL schema.
|
||||
Go to the ``Actions`` tab on the console and click on ``Create``. This will
|
||||
take you to a page like this:
|
||||
|
||||
.. thumbnail:: /img/graphql/manual/actions/mutation-action-create.png
|
||||
.. thumbnail:: /img/graphql/core/actions/mutation-action-create.png
|
||||
:alt: Console action create
|
||||
:width: 70%
|
||||
|
||||
@ -275,7 +275,7 @@ the GraphQL schema.
|
||||
Go to the ``Actions`` tab on the console and click on ``Create``. This will
|
||||
take you to a page like this:
|
||||
|
||||
.. thumbnail:: /img/graphql/manual/actions/query-action-create.png
|
||||
.. thumbnail:: /img/graphql/core/actions/query-action-create.png
|
||||
:alt: Console action create
|
||||
:width: 70%
|
||||
|
@ -75,13 +75,13 @@ derive our action:
|
||||
|
||||
Next hit the ``Derive action`` button as shown below:
|
||||
|
||||
.. thumbnail:: /img/graphql/manual/actions/actions-derive-button.png
|
||||
.. thumbnail:: /img/graphql/core/actions/actions-derive-button.png
|
||||
:alt: Console derive action button
|
||||
|
||||
This will redirect you to the ``Add a new action`` page with the action
|
||||
definition auto filled.
|
||||
|
||||
.. thumbnail:: /img/graphql/manual/actions/actions-derive-types.png
|
||||
.. thumbnail:: /img/graphql/core/actions/actions-derive-types.png
|
||||
:alt: Console derived action types
|
||||
|
||||
|
||||
@ -149,7 +149,7 @@ the action back to the original operation.
|
||||
You can select the framework of your choice to get the corresponding
|
||||
handler boilerplate code.
|
||||
|
||||
.. thumbnail:: /img/graphql/manual/actions/actions-derive-codegen.png
|
||||
.. thumbnail:: /img/graphql/core/actions/actions-derive-codegen.png
|
||||
:alt: Console derived action codegen
|
||||
|
||||
.. note::
|
@ -20,7 +20,7 @@ logic using custom queries and mutations. Actions can be
|
||||
added to Hasura to handle various use cases such as data validation, data
|
||||
enrichment from external sources and any other complex business logic.
|
||||
|
||||
.. thumbnail:: /img/graphql/manual/actions/actions-arch.png
|
||||
.. thumbnail:: /img/graphql/core/actions/actions-arch.png
|
||||
:class: no-shadow
|
||||
:alt: Actions high level architecture
|
||||
|
@ -98,7 +98,7 @@ You can create relationships for custom output types by:
|
||||
|
||||
Set the output type relationship as shown below:
|
||||
|
||||
.. thumbnail:: /img/graphql/manual/actions/actions-relationship.png
|
||||
.. thumbnail:: /img/graphql/core/actions/actions-relationship.png
|
||||
:alt: Console action relationship
|
||||
|
||||
Hit ``Save`` to create the relationship.
|
@ -12,6 +12,9 @@ Config API Reference
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The Config API is an admin only endpoint which gives info on the server
|
||||
configuration.
|
||||
|
@ -12,6 +12,9 @@ Explain API Reference
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The Explain API is used to analyse queries and subscriptions. It returns a list of Postgres plans for a query and a single Postgres plan for a subscription, based
|
||||
on the defined permissions.
|
||||
|
@ -12,6 +12,9 @@ GraphQL API Reference
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
All GraphQL requests for queries, subscriptions and mutations are made to the GraphQL API.
|
||||
|
||||
Endpoint
|
@ -12,6 +12,9 @@ Health check API Reference
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The Health API is a public endpoint which gives info on the server health.
|
||||
|
||||
Endpoint
|
@ -12,6 +12,9 @@ PG Dump API Reference
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The PG Dump API is an admin-only endpoint that can be used to execute ``pg_dump`` on the
|
||||
Postgres instance that Hasura is configured with.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user