console: unify webhook handler UX for action, remote schemas and events

[GS-397]: https://hasurahq.atlassian.net/browse/GS-397?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7796
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Sean Park-Ross <94021366+seanparkross@users.noreply.github.com>
GitOrigin-RevId: 620aea50e7d1b45835a5996246f46017b2ba5904
This commit is contained in:
Varun Choudhary 2023-02-16 15:42:51 +05:30 committed by hasura-bot
parent ac475fa02e
commit fc40739f62
22 changed files with 299 additions and 214 deletions

View File

@ -78,24 +78,24 @@ X-Hasura-Role: admin
### Args syntax {#metadata-pg-create-event-trigger-syntax}
| Key | Required | Schema | Description |
| ---------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| name | true | [TriggerName](/api-reference/syntax-defs.mdx#triggername) | Name of the Event Trigger |
| table | true | [QualifiedTable](/api-reference/syntax-defs.mdx#qualifiedtable) | Object with table name and schema |
| source | false | [SourceName](/api-reference/syntax-defs.mdx#sourcename) | Name of the source database of the table (default: `default`) |
| webhook | false | String | Full url of webhook (\*) |
| webhook_from_env | false | String | Environment variable name of webhook (must exist at boot time) (\*) |
| insert | false | [OperationSpec](/api-reference/syntax-defs.mdx#operationspec) | Specification for insert operation |
| update | false | [OperationSpec](/api-reference/syntax-defs.mdx#operationspec) | Specification for update operation |
| delete | false | [OperationSpec](/api-reference/syntax-defs.mdx#operationspec) | Specification for delete operation |
| headers | false | [ [HeaderFromValue](/api-reference/syntax-defs.mdx#headerfromvalue) \| [HeaderFromEnv](/api-reference/syntax-defs.mdx#headerfromenv) ] | List of headers to be sent with the webhook |
| retry_conf | false | [RetryConf](/api-reference/syntax-defs.mdx#retryconf) | Retry configuration if event delivery fails |
| replace | false | Boolean | If set to true, the Event Trigger is replaced with the new definition |
| enable_manual | false | Boolean | If set to true, the Event Trigger can be invoked manually |
| request_transform | false | [RequestTransformation](/api-reference/syntax-defs.mdx#requesttransformation) | Attaches a Request Transformation to the Event Trigger. |
| response_transform | false | [ResponseTransformation](/api-reference/syntax-defs.mdx#responsetransformation) | Attaches a Request Transformation to the Event Trigger. |
| cleanup_config | false | [AutoEventTriggerCleanupConfig](/api-reference/syntax-defs.mdx#autoeventtriggercleanupconfig) | Cleanup config for the auto cleanup process (EE/Cloud only). |
| trigger_on_replication | false | Boolean | Specification for enabling/disabling the Event Trigger during logical replication |
| Key | Required | Schema | Description |
|------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------|
| name | true | [TriggerName](/api-reference/syntax-defs.mdx#triggername) | Name of the Event Trigger |
| table | true | [QualifiedTable](/api-reference/syntax-defs.mdx#qualifiedtable) | Object with table name and schema |
| source | false | [SourceName](/api-reference/syntax-defs.mdx#sourcename) | Name of the source database of the table (default: `default`) |
| webhook | false | [WebhookURL](/api-reference/syntax-defs.mdx#webhookurl) | Event Trigger webhook URL |
| webhook_from_env | false | String | Environment variable name of webhook (Deprecated in favour of [WebhookURL](/api-reference/syntax-defs.mdx#webhookurl)) |
| insert | false | [OperationSpec](/api-reference/syntax-defs.mdx#operationspec) | Specification for insert operation |
| update | false | [OperationSpec](/api-reference/syntax-defs.mdx#operationspec) | Specification for update operation |
| delete | false | [OperationSpec](/api-reference/syntax-defs.mdx#operationspec) | Specification for delete operation |
| headers | false | [ [HeaderFromValue](/api-reference/syntax-defs.mdx#headerfromvalue) \| [HeaderFromEnv](/api-reference/syntax-defs.mdx#headerfromenv) ] | List of headers to be sent with the webhook |
| retry_conf | false | [RetryConf](/api-reference/syntax-defs.mdx#retryconf) | Retry configuration if event delivery fails |
| replace | false | Boolean | If set to true, the Event Trigger is replaced with the new definition |
| enable_manual | false | Boolean | If set to true, the Event Trigger can be invoked manually |
| request_transform | false | [RequestTransformation](/api-reference/syntax-defs.mdx#requesttransformation) | Attaches a Request Transformation to the Event Trigger. |
| response_transform | false | [ResponseTransformation](/api-reference/syntax-defs.mdx#responsetransformation) | Attaches a Request Transformation to the Event Trigger. |
| cleanup_config | false | [AutoEventTriggerCleanupConfig](/api-reference/syntax-defs.mdx#autoeventtriggercleanupconfig) | Cleanup config for the auto cleanup process (EE/Cloud only). |
| trigger_on_replication | false | Boolean | Specification for enabling/disabling the Event Trigger during logical replication |
(\*) Either `webhook` or `webhook_from_env` are required.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 454 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 139 KiB

View File

@ -5,21 +5,8 @@ import {
} from '../Common/stateDefaults';
import { DefaultState } from './types';
let defaultHandler = '';
if (typeof navigator !== 'undefined') {
const { appVersion } = navigator;
const isLinux =
appVersion.toLowerCase().includes('linux') ||
appVersion.toLowerCase().includes('x11');
if (isLinux) {
defaultHandler = 'http://localhost:3000';
} else {
defaultHandler = 'http://host.docker.internal:3000';
}
}
const state: DefaultState = {
handler: defaultHandler,
handler: '',
actionDefinition: {
sdl: defaultActionDefSdl,
error: null,

View File

@ -2,6 +2,9 @@ import React, { useEffect, useState } from 'react';
import { useDebouncedEffect } from '@/hooks/useDebounceEffect';
import { Analytics, REDACT_EVERYTHING } from '@/features/Analytics';
import { inputStyles } from '../../constants';
import { FaShieldAlt } from 'react-icons/fa';
import { IconTooltip } from '@/new-components/Tooltip';
import { LearnMoreLink } from '@/new-components/LearnMoreLink';
const editorLabel = 'Webhook (HTTP/S) Handler';
@ -32,26 +35,30 @@ const HandlerEditor: React.FC<HandlerEditorProps> = ({
return (
<Analytics name="ActionEditor" {...REDACT_EVERYTHING}>
<div className="mb-lg w-4/12">
<div className="mb-lg w-6/12">
<h2 className="text-lg font-semibold mb-xs flex items-center">
{editorLabel}
<span className="text-red-700 ml-xs mr-sm">*</span>
<span className="text-red-700 ml-xs">*</span>
<IconTooltip
message="Environment variables and secrets are available using the {{VARIABLE}} tag. Environment variable templating is available for this field. Example: https://{{ENV_VAR}}/endpoint_url"
icon={<FaShieldAlt className="h-4 text-muted cursor-pointer" />}
/>
<LearnMoreLink href="https://hasura.io/docs/latest/api-reference/syntax-defs/#webhookurl" />
</h2>
<p className="text-sm text-gray-600 mb-sm">
Note: Provide an URL or use an env var to template the handler URL if
you have different URLs for multiple environments.
</p>
<input
disabled={disabled}
type="text"
name="handler"
value={localValue}
onChange={e => setLocalValue(e.target.value)}
placeholder="http://custom-logic.com/api"
placeholder="http://custom-logic.com/api or {{ACTION_BASE_URL}}/handler"
className={inputStyles}
data-test="action-create-handler-input"
/>
<p className="text-sm text-gray-600">
Note: You can use an env var to template the handler URL if you have
different URLs for multiple environments.
<br /> e.g. {'{{ACTION_BASE_URL}}/handler'}
</p>
</div>
</Analytics>
);

View File

@ -61,6 +61,7 @@ import {
RetryConf,
EventTriggerAutoCleanup,
} from '../../types';
import { useDebouncedEffect } from '@/hooks/useDebounceEffect';
interface Props extends InjectedProps {}
@ -229,49 +230,53 @@ const Add: React.FC<Props> = props => {
transformState.sessionVars,
]);
useEffect(() => {
requestBodyErrorOnChange('');
requestTransformedBodyOnChange('');
const onResponse = (data: Record<string, any>) => {
parseValidateApiData(
data,
requestBodyErrorOnChange,
undefined,
requestTransformedBodyOnChange
);
};
const options = getValidateTransformOptions({
version: transformState.version,
inputPayloadString: transformState.requestSampleInput,
webhookUrl: webhook.value,
envVarsFromContext: transformState.envVars,
sessionVarsFromContext: transformState.sessionVars,
transformerBody: transformState.requestBody,
isEnvVar: webhook.type === 'env',
});
if (!webhook.value) {
requestBodyErrorOnChange(
'Please configure your webhook handler to generate request body transform'
);
} else if (transformState.requestBody && webhook.value) {
dispatch(
requestAction(
Endpoints.metadata,
options,
useDebouncedEffect(
() => {
requestBodyErrorOnChange('');
requestTransformedBodyOnChange('');
const onResponse = (data: Record<string, any>) => {
parseValidateApiData(
data,
requestBodyErrorOnChange,
undefined,
undefined,
true,
true
)
).then(onResponse, onResponse); // parseValidateApiData will parse both success and error
}
}, [
transformState.requestSampleInput,
transformState.requestBody,
webhook,
transformState.envVars,
transformState.sessionVars,
]);
requestTransformedBodyOnChange
);
};
const options = getValidateTransformOptions({
version: transformState.version,
inputPayloadString: transformState.requestSampleInput,
webhookUrl: webhook.value,
envVarsFromContext: transformState.envVars,
sessionVarsFromContext: transformState.sessionVars,
transformerBody: transformState.requestBody,
isEnvVar: webhook.type === 'env',
});
if (!webhook.value) {
requestBodyErrorOnChange(
'Please configure your webhook handler to generate request body transform'
);
} else if (transformState.requestBody && webhook.value) {
dispatch(
requestAction(
Endpoints.metadata,
options,
undefined,
undefined,
true,
true
)
).then(onResponse, onResponse); // parseValidateApiData will parse both success and error
}
},
1000,
[
transformState.requestSampleInput,
transformState.requestBody,
webhook,
transformState.envVars,
transformState.sessionVars,
]
);
const createBtnText = 'Create Event Trigger';

View File

@ -18,9 +18,9 @@ import {
} from '../../types';
import ColumnList from '../Common/ColumnList';
import FormLabel from './FormLabel';
import DebouncedDropdownInput from '../Common/DropdownWrapper';
import { inputStyles, heading } from '../../constants';
import { AutoCleanupForm } from '../Common/AutoCleanupForm';
import { FaShieldAlt } from 'react-icons/fa';
type CreateETFormProps = {
state: LocalEventTriggerState;
@ -62,7 +62,6 @@ const CreateETForm: React.FC<CreateETFormProps> = props => {
handleDatabaseChange,
handleSchemaChange,
handleTableChange,
handleWebhookTypeChange,
handleWebhookValueChange,
handleOperationsChange,
handleOperationsColumnsChange,
@ -166,36 +165,31 @@ const CreateETForm: React.FC<CreateETFormProps> = props => {
<FormLabel
title="Webhook (HTTP/S) Handler"
tooltip={tooltip.webhookUrlDescription}
tooltipIcon={
<FaShieldAlt className="h-4 text-muted cursor-pointer" />
}
knowMoreLink="https://hasura.io/docs/latest/api-reference/syntax-defs/#webhookurl"
/>
<div>
<div className="w-72">
<DebouncedDropdownInput
dropdownOptions={[
{ display_text: 'URL', value: 'static' },
{ display_text: 'From env var', value: 'env' },
]}
title={webhook.type === 'static' ? 'URL' : 'From env var'}
dataKey={webhook.type === 'static' ? 'static' : 'env'}
onButtonChange={handleWebhookTypeChange}
onHandlerValChange={handleWebhookValueChange}
<div className="w-1/2">
<p className="text-sm text-gray-600 mb-sm">
Note: Provide an URL or use an env var to template the handler URL
if you have different URLs for multiple environments.
</p>
<input
type="text"
name="handler"
onChange={e => handleWebhookValueChange(e.target.value)}
required
bsClass="w-72"
handlerVal={webhook.value}
value={webhook.value}
id="webhook-url"
inputPlaceHolder={
webhook.type === 'static'
? 'http://httpbin.org/post'
: 'MY_WEBHOOK_URL'
}
testId="webhook"
placeholder="http://httpbin.org/post or {{MY_WEBHOOK_URL}}/handler"
data-test="webhook"
className={`w-82 ${inputStyles}`}
/>
</div>
</div>
<br />
<small>
Note: Specifying the webhook URL via an environmental variable is
recommended if you have different URLs for multiple environments.
</small>
</div>
<hr className="my-md" />
{isProConsole(window.__env) && (

View File

@ -1,17 +1,26 @@
import React from 'react';
import { IconTooltip } from '@/new-components/Tooltip';
import { LearnMoreLink } from '@/new-components/LearnMoreLink';
type FormLabelProps = {
title: string;
tooltip: string;
tooltipIcon?: React.ReactElement;
knowMoreLink?: string;
};
const FormLabel: React.FC<FormLabelProps> = ({ title, tooltip }) => {
const FormLabel: React.FC<FormLabelProps> = ({
title,
tooltip,
tooltipIcon,
knowMoreLink,
}) => {
return (
<>
<h2 className="text-lg font-semibold mb-xs flex items-center">
{title}
<IconTooltip message={tooltip} />
<IconTooltip message={tooltip} icon={tooltipIcon} />
{knowMoreLink ? <LearnMoreLink href={knowMoreLink} /> : null}
</h2>
</>
);

View File

@ -12,7 +12,7 @@ export const manualOperationsDescription =
'Trigger manually from table data browser in console';
export const webhookUrlDescription =
'POST endpoint which will be triggered with payload on configured events';
'Environment variables and secrets are available using the {{VARIABLE}} tag. Environment variable templating is available for this field. Example: https://{{ENV_VAR}}/endpoint_url';
export const advancedOperationDescription =
'For update triggers, webhook will be triggered only when selected columns are modified';

View File

@ -65,6 +65,7 @@ import {
getEventTriggerByName,
} from '../../../../../metadata/selector';
import { AutoCleanupForm } from '../Common/AutoCleanupForm';
import { useDebouncedEffect } from '@/hooks/useDebounceEffect';
interface Props extends InjectedProps {}
@ -291,25 +292,29 @@ const Modify: React.FC<Props> = props => {
transformState.sessionVars,
]);
useEffect(() => {
if (
transformState.requestBody &&
state.webhook?.value &&
!transformState.requestTransformedBody
) {
requestBodyErrorOnChange('');
dispatch(
requestAction(
Endpoints.metadata,
reqBodyoptions,
undefined,
undefined,
true,
true
)
).then(onRequestBodyResponse, onRequestBodyResponse);
}
}, [transformState.requestTransformedBody]);
useDebouncedEffect(
() => {
if (
transformState.requestBody &&
state.webhook?.value &&
!transformState.requestTransformedBody
) {
requestBodyErrorOnChange('');
dispatch(
requestAction(
Endpoints.metadata,
reqBodyoptions,
undefined,
undefined,
true,
true
)
).then(onRequestBodyResponse, onRequestBodyResponse);
}
},
1000,
[transformState.requestTransformedBody]
);
const saveWrapper =
(property?: EventTriggerProperty) =>

View File

@ -1,8 +1,10 @@
import React from 'react';
import { FaShieldAlt } from 'react-icons/fa';
import Editor from '../../../../Common/Layout/ExpandableEditor/Editor';
import { inputStyles } from '../../constants';
import { EventTrigger, URLConf, VoidCallback } from '../../types';
import { parseServerWebhook } from '../../utils';
import FormLabel from '../Add/FormLabel';
import DebouncedDropdownInput from '../Common/DropdownWrapper';
type WebhookEditorProps = {
@ -14,7 +16,6 @@ type WebhookEditorProps = {
const WebhookEditor = (props: WebhookEditorProps) => {
const { currentTrigger, webhook, setWebhook, save } = props;
const existingWebhook = parseServerWebhook(
currentTrigger.configuration.webhook,
currentTrigger.configuration.webhook_from_env
@ -50,37 +51,62 @@ const WebhookEditor = (props: WebhookEditorProps) => {
);
const expanded = () => (
<div className="pb-sm pt-sm max-w-80">
<DebouncedDropdownInput
dropdownOptions={[
{ display_text: 'URL', value: 'static' },
{ display_text: 'From env var', value: 'env' },
]}
title={webhook.type === 'env' ? 'From env var' : 'URL'}
dataKey={webhook.type === 'env' ? 'env' : 'static'}
onButtonChange={handleWebhookTypeChange}
onHandlerValChange={handleWebhookValueChange}
required
bsClass={`${inputStyles} w-72`}
handlerVal={webhook.value}
id="webhook-url"
inputPlaceHolder={
webhook.type === 'env' ? 'MY_WEBHOOK_URL' : 'http://httpbin.org/post'
}
testId="webhook"
/>
<div className="w-1/2">
<p className="text-sm text-gray-600 mb-sm">
Note: Provide an URL or use an env var to template the handler URL if
you have different URLs for multiple environments.
</p>
{existingWebhook?.type === 'env' ? (
<DebouncedDropdownInput
dropdownOptions={[
{ display_text: 'URL', value: 'static' },
{ display_text: 'From env var', value: 'env' },
]}
title={webhook.type === 'env' ? 'From env var' : 'URL'}
dataKey={webhook.type === 'env' ? 'env' : 'static'}
onButtonChange={handleWebhookTypeChange}
onHandlerValChange={handleWebhookValueChange}
required
bsClass={`w-82`}
handlerVal={webhook.value}
id="webhook-url"
inputPlaceHolder={
webhook.type === 'env'
? 'MY_WEBHOOK_URL'
: 'http://httpbin.org/post'
}
testId="webhook"
/>
) : (
<input
type="text"
name="handler"
onChange={e => handleWebhookValueChange(e.target.value)}
required
className={`${inputStyles} w-82`}
value={
webhook.type === 'static' ? webhook.value : `{{${webhook.value}}}`
}
id="webhook-url"
placeholder="http://httpbin.org/post or {{MY_WEBHOOK_URL}}/handler"
data-test="webhook"
/>
)}
<br />
<small>
Note: Specifying the webhook URL via an environmental variable is
recommended if you have different URLs for multiple environments.
</small>
</div>
);
return (
<div className="w-full border-b border-solid border-gray-300 mb-md">
<div className="mb-md">
<h4 className="text-lg font-bold mb-md">Webhook (HTTP/S) Handler</h4>
<FormLabel
title="Webhook (HTTP/S) Handler"
tooltip="Environment variables and secrets are available using the {{VARIABLE}} tag. Environment variable templating is available for this field. Example: https://{{ENV_VAR}}/endpoint_url"
tooltipIcon={
<FaShieldAlt className="h-4 text-muted cursor-pointer" />
}
knowMoreLink="https://hasura.io/docs/latest/api-reference/syntax-defs/#webhookurl"
/>
<Editor
editorCollapsed={collapsed}
editorExpanded={expanded}

View File

@ -12,6 +12,8 @@ import CommonHeader from '../../../Common/Layout/ReusableHeader/Header';
import GraphQLCustomizationEdit from './GraphQLCustomization/GraphQLCustomizationEdit';
import { focusYellowRing, inputStyles, subHeading } from '../constants';
import { FaShieldAlt } from 'react-icons/fa';
import { LearnMoreLink } from '@/new-components/LearnMoreLink';
class Common extends React.Component {
getPlaceHolderText(valType) {
@ -48,6 +50,7 @@ class Common extends React.Component {
forwardClientHeaders,
comment,
customization,
isEnvVarEnabled,
} = this.props;
const { isModify } = this.props.editState;
@ -58,8 +61,9 @@ class Common extends React.Component {
const tooltips = {
graphqlurl: (
<IconTooltip
message="Remote GraphQL servers URL. E.g. https://my-domain/v1/graphql"
message="Environment variables and secrets are available using the {{VARIABLE}} tag. Environment variable templating is available for this field. Example: https://{{ENV_VAR}}/endpoint_url"
side="right"
icon={<FaShieldAlt className="h-4 text-muted cursor-pointer" />}
/>
),
clientHeaderForward: (
@ -142,42 +146,59 @@ class Common extends React.Component {
<hr className="my-md" />
<div className={`${subHeading} flex items-center`}>
GraphQL server URL *{tooltips.graphqlurl}
<LearnMoreLink href="https://hasura.io/docs/latest/api-reference/syntax-defs/#webhookurl" />
</div>
<div className={'w-80'}>
<DropdownButton
dropdownOptions={[
{ display_text: 'URL', value: 'manualUrl' },
{ display_text: 'From env var', value: 'envName' },
]}
title={
(manualUrl !== null && 'URL') ||
(envName !== null && 'From env var') ||
'Value'
}
dataKey={
(manualUrl !== null && 'manualUrl') ||
(envName !== null && 'envName')
}
onButtonChange={this.toggleUrlParam.bind(this)}
onInputChange={this.handleInputChange.bind(this)}
required={urlRequired}
bsClass="w-80"
inputVal={manualUrl || envName}
disabled={isDisabled}
id="graphql-server-url"
inputPlaceHolder={
(manualUrl !== null &&
'https://my-graphql-service.com/graphql') ||
(envName !== null && 'MY_GRAPHQL_ENDPOINT')
}
testId="remote-schema-graphql-url"
/>
<p className="text-sm text-gray-600 mb-sm w-1/2">
Note: Provide an URL or use an env var to template the handler URL if
you have different URLs for multiple environments.
</p>
<div className={'w-1/2'}>
{isEnvVarEnabled ? (
<DropdownButton
dropdownOptions={[
{ display_text: 'URL', value: 'manualUrl' },
{ display_text: 'From env var', value: 'envName' },
]}
title={
(manualUrl !== null && 'URL') ||
(envName !== null && 'From env var') ||
'Value'
}
dataKey={
(manualUrl !== null && 'manualUrl') ||
(envName !== null && 'envName')
}
onButtonChange={this.toggleUrlParam.bind(this)}
onInputChange={this.handleInputChange.bind(this)}
required={urlRequired}
bsClass="w-80"
inputVal={envName || manualUrl}
disabled={isDisabled}
id="graphql-server-url"
inputPlaceHolder={
(manualUrl !== null &&
'https://my-graphql-service.com/graphql') ||
(envName !== null && 'MY_GRAPHQL_ENDPOINT')
}
testId="remote-schema-graphql-url"
/>
) : (
<input
type="text"
name="handler"
onChange={this.handleInputChange.bind(this)}
required={urlRequired}
className={`w-3/4 ${inputStyles}`}
data-key={'manualUrl'}
value={manualUrl}
disabled={isDisabled}
id="graphql-server-url"
placeholder="https://my-graphql-service.com/graphql or {{MY_WEBHOOK_URL}}/graphql"
data-test="remote-schema-graphql-url"
/>
)}
</div>
<br />
<small>
Note: Specifying the server URL via an environmental variable is
recommended if you have different URLs for multiple environments.
</small>
<div className={`${subHeading} pt-md`}>
Headers for the remote GraphQL server
</div>

View File

@ -199,6 +199,12 @@ class Edit extends React.Component {
{ redactText: true }
);
const isEnvVarEnabled = () => {
return this.props.allRemoteSchemas.some(
rs => rs.name === remoteSchemaName && rs.definition.url_from_env
);
};
return (
<div>
<Helmet>
@ -227,7 +233,7 @@ class Edit extends React.Component {
this.editClicked();
}}
>
<Common {...this.props} />
<Common {...this.props} isEnvVarEnabled={isEnvVarEnabled()} />
{generateMigrateBtns()}
</form>
</Analytics>

View File

@ -11,6 +11,7 @@ import {
ScheduleEventPayloadInput,
} from './components';
import { ScheduledTime } from './components/ScheduledTime';
import { FaShieldAlt } from 'react-icons/fa';
type Props = {
/**
@ -51,10 +52,15 @@ const OneOffScheduledEventForm = (props: Props) => {
</div>
<div className="mb-md">
<InputField
learnMoreLink="https://hasura.io/docs/latest/api-reference/syntax-defs/#webhookurl"
tooltipIcon={
<FaShieldAlt className="h-4 text-muted cursor-pointer" />
}
name="webhook"
label="Webhook URL"
placeholder="https://httpbin.com/post"
tooltip="The HTTP URL that should be triggered."
placeholder="https://httpbin.com/post or {{MY_WEBHOOK_URL}}/handler"
tooltip="Environment variables and secrets are available using the {{VARIABLE}} tag. Environment variable templating is available for this field. Example: https://{{ENV_VAR}}/endpoint_url"
description="Note: Provide an URL or use an env var to template the handler URL if you have different URLs for multiple environments."
/>
</div>
<div className="mb-md">

View File

@ -19,6 +19,7 @@ import {
} from './utils';
import { useCronMetadataMigration, useDefaultValues } from './hooks';
import { CronRequestTransformation } from './components/CronRequestTransformation';
import { FaQuestionCircle, FaShieldAlt } from 'react-icons/fa';
type Props = {
/**
@ -63,10 +64,15 @@ const FormContent = (props: FormContentProps) => {
<hr className="my-md" />
<div className="mb-xs w-1/2">
<InputField
learnMoreLink="https://hasura.io/docs/latest/api-reference/syntax-defs/#webhookurl"
tooltipIcon={
<FaShieldAlt className="h-4 text-muted cursor-pointer" />
}
name="webhook"
label="Webhook URL"
placeholder="https://httpbin.com/post"
tooltip="The HTTP URL that should be triggered. You can also provide the URL from environment variables, e.g. {{MY_WEBHOOK_URL}}"
placeholder="https://httpbin.com/post or {{MY_WEBHOOK_URL}}/handler"
tooltip="Environment variables and secrets are available using the {{VARIABLE}} tag. Environment variable templating is available for this field. Example: https://{{ENV_VAR}}/endpoint_url"
description="Note: Provide an URL or use an env var to template the handler URL if you have different URLs for multiple environments."
/>
</div>
<div className="mb-xs w-1/2">

View File

@ -16,7 +16,9 @@ export const CronScheduleSelector = () => {
return (
<>
<div className="block flex items-center text-gray-600 font-semibold">
<label htmlFor="schedule">Cron Schedule</label>
<label htmlFor="schedule" className="font-semibold">
Cron Schedule
</label>
<IconTooltip message="Schedule for your cron (events are created based on the UTC timezone)" />
<LearnMoreLink
href="https://crontab.guru/#*_*_*_*_*"

View File

@ -1,6 +1,8 @@
import React from 'react';
import { IconTooltip } from '@/new-components/Tooltip';
import { useFormContext } from 'react-hook-form';
import { FaShieldAlt } from 'react-icons/fa';
import { LearnMoreLink } from '@/new-components/LearnMoreLink';
export const GraphQLServiceUrl = () => {
const { watch, register } = useFormContext();
@ -9,28 +11,21 @@ export const GraphQLServiceUrl = () => {
<div className="mb-md w-6/12">
<label className="block flex items-center text-gray-600 font-semibold mb-xs">
GraphQL Service URL
<IconTooltip message="Remote GraphQL servers URL. E.g. https://my-domain/v1/graphql" />
<IconTooltip
message="Environment variables and secrets are available using the {{VARIABLE}} tag. Environment variable templating is available for this field. Example: https://{{ENV_VAR}}/endpoint_url"
icon={<FaShieldAlt className="h-4 text-muted cursor-pointer" />}
/>
<LearnMoreLink href="https://hasura.io/docs/latest/api-reference/syntax-defs/#webhookurl" />
</label>
<p className="text-sm text-gray-600 mb-sm">
Note: Specifying the server URL via an environmental variable is
recommended if you have different URLs for multiple environments.
Note: Provide an URL or use an env var to template the handler URL if
you have different URLs for multiple environments.
</p>
<div className="flex shadow-sm rounded">
<select
className="inline-flex rounded-l border border-r-0 border-gray-300 bg-white hover:border-gray-400 focus:ring-2 focus:ring-yellow-200 focus:border-yellow-400"
{...register('url.type')}
>
<option value="from_url">URL</option>
<option value="from_env">Env Var</option>
</select>
<input
type="text"
className="flex-1 min-w-0 block w-full px-3 py-2 rounded-r border-gray-300 hover:border-gray-400 focus:ring-2 focus:ring-yellow-200 focus:border-yellow-400"
placeholder={
url?.type === 'from_url'
? 'https://myservice.com/graphql'
: 'MY_GRAPHQL_ENDPOINT'
}
placeholder="https://myservice.com/graphql or {{MY_WEBHOOK_URL}}/graphql"
{...register('url.value')}
data-testid="url"
/>

View File

@ -39,6 +39,10 @@ export type FieldWrapperPassThroughProps = {
* Render line breaks in the description
*/
renderDescriptionLineBreaks?: boolean;
/**
* tooltip icon other then ?
*/
tooltipIcon?: React.ReactElement;
} & DiscriminatedTypes<
{
/**
@ -110,6 +114,7 @@ export const FieldWrapper = (props: FieldWrapperProps) => {
className,
size = 'full',
error,
tooltipIcon,
children,
description,
tooltip,
@ -158,7 +163,9 @@ export const FieldWrapper = (props: FieldWrapperProps) => {
{label}
{loading ? <Skeleton className="absolute inset-0" /> : null}
</span>
{!loading && tooltip ? <IconTooltip message={tooltip} /> : null}
{!loading && tooltip ? (
<IconTooltip message={tooltip} icon={tooltipIcon} />
) : null}
{!loading && !!learnMoreLink && (
<LearnMoreLink href={learnMoreLink} />
)}

View File

@ -18,7 +18,7 @@ export const LearnMoreLink: React.VFC<LearnMoreLinkProps> = props => {
href={href}
target="_blank"
rel="noopener noreferrer"
className={`ml-xs italic text-sm text-secondary ${className}`}
className={`ml-xs italic text-sm font-thin text-secondary ${className}`}
>
{text}
</a>

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { ReactElement, ReactNode } from 'react';
import { Tooltip, TooltipProps } from '@/new-components/Tooltip';
import { FaQuestionCircle } from 'react-icons/fa';
@ -11,6 +11,10 @@ export type IconTooltipProps = {
* The tooltip icon classes
*/
className?: string;
/**
* tooltip icon other then ?
*/
icon?: ReactNode;
} & Pick<TooltipProps, 'side'> &
Pick<TooltipProps, 'defaultOpen'>;
@ -19,6 +23,7 @@ export const IconTooltip: React.VFC<IconTooltipProps> = ({
className,
side = 'right',
defaultOpen = false,
icon,
}) => (
<Tooltip
tooltipContentChildren={message}
@ -26,8 +31,12 @@ export const IconTooltip: React.VFC<IconTooltipProps> = ({
defaultOpen={defaultOpen}
className="flex items-center"
>
<FaQuestionCircle
className={`h-4 text-muted cursor-pointer ${className}`}
/>
{!icon ? (
<FaQuestionCircle
className={`h-4 text-muted cursor-pointer ${className}`}
/>
) : (
icon
)}
</Tooltip>
);