Added help text where needed on connectors and alert actions UI (#69601)

* Added help text where needed on connectors and alert actions UI

* fixed ui form

* Added index action type examples, fixed slack link

* Fixed email connector docs and links

* Additional cleanup on email

* Removed autofocus to avoid twice link click for opening in the new page

* Extended documentation for es index action type

* Fixed tests

* Fixed doc link

* fixed due to comments

* fixed due to comments

* Update docs/user/alerting/action-types/email.asciidoc

Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>

* Update docs/user/alerting/action-types/email.asciidoc

Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>

* Update docs/user/alerting/action-types/email.asciidoc

Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>

* Update docs/user/alerting/action-types/email.asciidoc

Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>

* Update docs/user/alerting/action-types/email.asciidoc

Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>

* Update docs/user/alerting/action-types/email.asciidoc

Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>

* Update docs/user/alerting/action-types/email.asciidoc

Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/actions/README.md

Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/actions/README.md

Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>

* Update docs/user/alerting/action-types/email.asciidoc

Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>

* Update docs/user/alerting/action-types/email.asciidoc

Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>

* Update docs/user/alerting/action-types/email.asciidoc

Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>

* Update docs/user/alerting/action-types/email.asciidoc

Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>

* Update docs/user/alerting/action-types/index.asciidoc

Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>

* Update docs/user/alerting/action-types/slack.asciidoc

Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>

* Fixed due to comments

Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>
This commit is contained in:
Yuliia Naumenko 2020-07-13 19:53:37 -07:00 committed by GitHub
parent 835c13dd6a
commit 2009447ab8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 288 additions and 43 deletions

View file

@ -77,3 +77,122 @@ Email actions have the following configuration properties:
To, CC, BCC:: Each is a list of addresses. Addresses can be specified in `user@host-name` format, or in `name <user@host-name>` format. One of To, CC, or BCC must contain an entry.
Subject:: The subject line of the email.
Message:: The message text of the email. Markdown format is supported.
[[configuring-email]]
==== Configuring email accounts
The email action can send email using many popular SMTP email services.
You configure the email action to send emails using the connector form.
For more information about configuring the email connector to work with different email
systems, refer to:
* <<gmail>>
* <<outlook>>
* <<exchange>>
* <<amazon-ses>>
[float]
[[gmail]]
===== Sending email from Gmail
Use the following email account settings to send email from the
https://mail.google.com[Gmail] SMTP service:
[source,text]
--------------------------------------------------
config:
host: smtp.gmail.com
port: 465
secure: true
secrets:
user: <username>
password: <password>
--------------------------------------------------
// CONSOLE
If you get an authentication error that indicates that you need to continue the
sign-in process from a web browser when the action attempts to send email, you need
to configure Gmail to https://support.google.com/accounts/answer/6010255?hl=en[allow
less secure apps to access your account].
If two-step verification is enabled for your account, you must generate and use
a unique App Password to send email from {watcher}. See
https://support.google.com/accounts/answer/185833?hl=en[Sign in using App Passwords]
for more information.
[float]
[[outlook]]
===== Sending email from Outlook.com
Use the following email account settings to send email action from the
https://www.outlook.com/[Outlook.com] SMTP service:
[source,text]
--------------------------------------------------
config:
host: smtp-mail.outlook.com
port: 465
secure: true
secrets:
user: <email.address>
password: <password>
--------------------------------------------------
When sending emails, you must provide a from address, either as the default
in your account configuration or as part of the email action in the watch.
NOTE: You must use a unique App Password if two-step verification is enabled.
See http://windows.microsoft.com/en-us/windows/app-passwords-two-step-verification[App
passwords and two-step verification] for more information.
[float]
[[amazon-ses]]
===== Sending email from Amazon SES (Simple Email Service)
Use the following email account settings to send email from the
http://aws.amazon.com/ses[Amazon Simple Email Service] (SES) SMTP service:
[source,text]
--------------------------------------------------
config:
host: email-smtp.us-east-1.amazonaws.com <1>
port: 465
secure: true
secrets:
user: <username>
password: <password>
--------------------------------------------------
<1> `smtp.host` varies depending on the region
NOTE: You must use your Amazon SES SMTP credentials to send email through
Amazon SES. For more information, see
http://docs.aws.amazon.com/ses/latest/DeveloperGuide/smtp-credentials.html[Obtaining
Your Amazon SES SMTP Credentials]. You might also need to verify
https://docs.aws.amazon.com/ses/latest/DeveloperGuide/verify-email-addresses.html[your email address]
or https://docs.aws.amazon.com/ses/latest/DeveloperGuide/verify-domains.html[your whole domain]
at AWS.
[float]
[[exchange]]
===== Sending email from Microsoft Exchange
Use the following email account settings to send email action from Microsoft
Exchange:
[source,text]
--------------------------------------------------
config:
host: <your exchange server>
port: 465
secure: true
from: <email address of service account> <1>
secrets:
user: <email address of service account> <2>
password: <password>
--------------------------------------------------
<1> Some organizations configure Exchange to validate that the `from` field is a
valid local email account.
<2> Many organizations support use of your email address as your username.
Check with your system administrator if you receive
authentication-related failures.

View file

@ -2,7 +2,7 @@
[[index-action-type]]
=== Index action
The index action type will index a document into {es}.
The index action type will index a document into {es}. See also the {ref}/indices-create-index.html[create index API].
[float]
[[index-connector-configuration]]
@ -53,4 +53,38 @@ Execution time field:: This field will be automatically set to the time the ale
Index actions have the following properties:
Document:: The document to index in json format.
Document:: The document to index in JSON format.
Example of the index document for Index Threshold alert:
[source,text]
--------------------------------------------------
{
"alert_id": "{{alertId}}",
"alert_name": "{{alertName}}",
"alert_instance_id": "{{alertInstanceId}}",
"context_message": "{{context.message}}"
}
--------------------------------------------------
Example of create test index using the API.
[source,text]
--------------------------------------------------
PUT test
{
"settings" : {
"number_of_shards" : 1
},
"mappings" : {
"_doc" : {
"properties" : {
"alert_id" : { "type" : "text" },
"alert_name" : { "type" : "text" },
"alert_instance_id" : { "type" : "text" },
"context_message": { "type" : "text" }
}
}
}
}
--------------------------------------------------

View file

@ -38,3 +38,23 @@ Webhook URL:: The URL of the incoming webhook. See https://api.slack.com/messa
Slack actions have the following properties:
Message:: The message text, converted to the `text` field in the Webhook JSON payload. Currently only the text field is supported. Markdown, images, and other advanced formatting are not yet supported.
[[configuring-slack]]
==== Configuring Slack Accounts
You configure the accounts Slack action type can use to communicate with Slack in the
connector form.
You need a https://api.slack.com/incoming-webhooks[Slack webhook URL] to
configure a Slack account. To create a webhook
URL, set up an an **Incoming Webhook Integration** through the Slack console:
. Log in to http://slack.com[slack.com] as a team administrator.
. Go to https://my.slack.com/services/new/incoming-webhook.
. Select a default channel for the integration.
+
image::images/slack-add-webhook-integration.png[]
. Click *Add Incoming Webhook Integration*.
. Copy the generated webhook URL so you can paste it into your Slack connector form.
+
image::images/slack-copy-webhook-url.png[]

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View file

@ -437,9 +437,12 @@ The config and params properties are modelled after the [Watcher Index Action](h
### `config`
| Property | Description | Type |
| -------- | -------------------------------------- | ------------------- |
| index | The Elasticsearch index to index into. | string _(optional)_ |
| Property | Description | Type |
| -------------------- | ---------------------------------------------------------- | -------------------- |
| index | The Elasticsearch index to index into. | string _(optional)_ |
| doc_id | The optional \_id of the document. | string _(optional)_ |
| execution_time_field | The field that will store/index the action execution time. | string _(optional)_ |
| refresh | Setting of the refresh policy for the write request. | boolean _(optional)_ |
### `secrets`
@ -447,13 +450,9 @@ This action type has no `secrets` properties.
### `params`
| Property | Description | Type |
| -------------------- | ---------------------------------------------------------- | -------------------- |
| index | The Elasticsearch index to index into. | string _(optional)_ |
| doc_id | The optional \_id of the document. | string _(optional)_ |
| execution_time_field | The field that will store/index the action execution time. | string _(optional)_ |
| refresh | Setting of the refresh policy for the write request | boolean _(optional)_ |
| body | The documument body/bodies to index. | object or object[] |
| Property | Description | Type |
| --------- | ---------------------------------------- | ------------------- |
| documents | JSON object that describes the [document](https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started-index.html#getting-started-batch-processing). | object[] |
---

View file

@ -900,10 +900,23 @@ export function getActionType(): ActionTypeModel {
![Index connector card](https://i.imgur.com/fflsmu5.png)
![Index connector form](https://i.imgur.com/tbgyvAL.png)
![Index connector form](https://i.imgur.com/IkixGMV.png)
and action params form available in Create Alert form:
![Index action form](https://i.imgur.com/VsWMLeU.png)
![Index action form](https://i.imgur.com/mpxnPOF.png)
Example of the index document for Index Threshold alert:
```
{
"alert_id": "{{alertId}}",
"alert_name": "{{alertName}}",
"alert_instance_id": "{{alertInstanceId}}",
"context_title": "{{context.title}}",
"context_value": "{{context.value}}",
"context_message": "{{context.message}}"
}
```
### Webhook
@ -1582,4 +1595,3 @@ export interface ActionsConnectorsContextValue {
|capabilities|Property, which is defining action current user usage capabilities like canSave or canDelete.|
|toastNotifications|Toast messages.|
|reloadConnectors|Optional function, which will be executed if connector was saved sucsessfuly, like reload list of connecotrs.|

View file

@ -14,12 +14,14 @@ import {
EuiFormRow,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiLink } from '@elastic/eui';
import { ActionConnectorFieldsProps } from '../../../../types';
import { EmailActionConnector } from '../types';
export const EmailActionConnectorFields: React.FunctionComponent<ActionConnectorFieldsProps<
EmailActionConnector
>> = ({ action, editActionConfig, editActionSecrets, errors }) => {
>> = ({ action, editActionConfig, editActionSecrets, errors, docLinks }) => {
const { from, host, port, secure } = action.config;
const { user, password } = action.secrets;
@ -38,6 +40,17 @@ export const EmailActionConnectorFields: React.FunctionComponent<ActionConnector
defaultMessage: 'Sender',
}
)}
helpText={
<EuiLink
href={`${docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${docLinks.DOC_LINK_VERSION}/email-action-type.html#configuring-email`}
target="_blank"
>
<FormattedMessage
id="xpack.triggersActionsUI.components.builtinActionTypes.emailAction.configureAccountsHelpLabel"
defaultMessage="Configuring email accounts."
/>
</EuiLink>
}
>
<EuiFieldText
fullWidth

View file

@ -5,6 +5,7 @@
*/
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { DocLinksStart } from 'kibana/public';
import EmailParamsFields from './email_params';
describe('EmailParamsFields renders', () => {
@ -22,6 +23,7 @@ describe('EmailParamsFields renders', () => {
errors={{ to: [], cc: [], bcc: [], subject: [], message: [] }}
editAction={() => {}}
index={0}
docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart}
/>
);
expect(wrapper.find('[data-test-subj="toEmailAddressInput"]').length > 0).toBeTruthy();

View file

@ -13,6 +13,7 @@ import {
EuiSelect,
EuiTitle,
EuiIconTip,
EuiLink,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
@ -28,7 +29,7 @@ import {
const IndexActionConnectorFields: React.FunctionComponent<ActionConnectorFieldsProps<
EsIndexActionConnector
>> = ({ action, editActionConfig, errors, http }) => {
>> = ({ action, editActionConfig, errors, http, docLinks }) => {
const { index, refresh, executionTimeField } = action.config;
const [hasTimeFieldCheckbox, setTimeFieldCheckboxState] = useState<boolean>(
executionTimeField != null
@ -77,10 +78,22 @@ const IndexActionConnectorFields: React.FunctionComponent<ActionConnectorFieldsP
isInvalid={errors.index.length > 0 && index !== undefined}
error={errors.index}
helpText={
<FormattedMessage
id="xpack.triggersActionsUI.components.builtinActionTypes.indexAction.howToBroadenSearchQueryDescription"
defaultMessage="Use * to broaden your query."
/>
<>
<FormattedMessage
id="xpack.triggersActionsUI.components.builtinActionTypes.indexAction.howToBroadenSearchQueryDescription"
defaultMessage="Use * to broaden your query."
/>
<EuiSpacer size="s" />
<EuiLink
href={`${docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${docLinks.DOC_LINK_VERSION}/index-action-type.html`}
target="_blank"
>
<FormattedMessage
id="xpack.triggersActionsUI.components.builtinActionTypes.indexAction.configureIndexHelpLabel"
defaultMessage="Configuring index connector."
/>
</EuiLink>
</>
}
>
<EuiComboBox
@ -186,9 +199,9 @@ const IndexActionConnectorFields: React.FunctionComponent<ActionConnectorFieldsP
</>
}
/>
<EuiSpacer size="m" />
{hasTimeFieldCheckbox ? (
<>
<EuiSpacer size="m" />
<EuiFormRow
id="executionTimeField"
fullWidth

View file

@ -6,6 +6,7 @@
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import ParamsFields from './es_index_params';
import { DocLinksStart } from 'kibana/public';
describe('IndexParamsFields renders', () => {
test('all params fields is rendered', () => {
@ -18,6 +19,7 @@ describe('IndexParamsFields renders', () => {
errors={{ index: [] }}
editAction={() => {}}
index={0}
docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart}
/>
);
expect(wrapper.find('[data-test-subj="documentsJsonEditor"]').first().prop('value')).toBe(`{

View file

@ -4,7 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { EuiLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { ActionParamsProps } from '../../../../types';
import { IndexActionParams } from '.././types';
import { JsonEditorWithMessageVariables } from '../../json_editor_with_message_variables';
@ -14,6 +16,7 @@ export const IndexParamsFields = ({
index,
editAction,
messageVariables,
docLinks,
}: ActionParamsProps<IndexActionParams>) => {
const { documents } = actionParams;
@ -26,26 +29,39 @@ export const IndexParamsFields = ({
};
return (
<JsonEditorWithMessageVariables
messageVariables={messageVariables}
paramsProperty={'documents'}
inputTargetValue={
documents && documents.length > 0 ? ((documents[0] as unknown) as string) : ''
}
label={i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.documentsFieldLabel',
{
defaultMessage: 'Document to index',
<>
<JsonEditorWithMessageVariables
messageVariables={messageVariables}
paramsProperty={'documents'}
inputTargetValue={
documents && documents.length > 0 ? ((documents[0] as unknown) as string) : ''
}
)}
aria-label={i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.jsonDocAriaLabel',
{
defaultMessage: 'Code editor',
label={i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.documentsFieldLabel',
{
defaultMessage: 'Document to index',
}
)}
aria-label={i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.jsonDocAriaLabel',
{
defaultMessage: 'Code editor',
}
)}
onDocumentsChange={onDocumentsChange}
helpText={
<EuiLink
href={`${docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${docLinks.DOC_LINK_VERSION}/index-action-type.html#index-action-configuration`}
target="_blank"
>
<FormattedMessage
id="xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indexDocumentHelpLabel"
defaultMessage="Index document example."
/>
</EuiLink>
}
)}
onDocumentsChange={onDocumentsChange}
/>
/>
</>
);
};

View file

@ -7,6 +7,7 @@ import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { EventActionOptions, SeverityActionOptions } from '.././types';
import PagerDutyParamsFields from './pagerduty_params';
import { DocLinksStart } from 'kibana/public';
describe('PagerDutyParamsFields renders', () => {
test('all params fields is rendered', () => {
@ -27,6 +28,7 @@ describe('PagerDutyParamsFields renders', () => {
errors={{ summary: [], timestamp: [] }}
editAction={() => {}}
index={0}
docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart}
/>
);
expect(wrapper.find('[data-test-subj="severitySelect"]').length > 0).toBeTruthy();

View file

@ -7,6 +7,7 @@ import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { ServerLogLevelOptions } from '.././types';
import ServerLogParamsFields from './server_log_params';
import { DocLinksStart } from 'kibana/public';
describe('ServerLogParamsFields renders', () => {
test('all params fields is rendered', () => {
@ -21,6 +22,7 @@ describe('ServerLogParamsFields renders', () => {
editAction={() => {}}
index={0}
defaultMessage={'test default message'}
docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart}
/>
);
expect(wrapper.find('[data-test-subj="loggingLevelSelect"]').length > 0).toBeTruthy();
@ -41,6 +43,7 @@ describe('ServerLogParamsFields renders', () => {
errors={{ message: [] }}
editAction={() => {}}
index={0}
docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart}
/>
);
expect(wrapper.find('[data-test-subj="loggingLevelSelect"]').length > 0).toBeTruthy();

View file

@ -6,6 +6,7 @@
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import ServiceNowParamsFields from './servicenow_params';
import { DocLinksStart } from 'kibana/public';
describe('ServiceNowParamsFields renders', () => {
test('all params fields is rendered', () => {
@ -29,6 +30,7 @@ describe('ServiceNowParamsFields renders', () => {
editAction={() => {}}
index={0}
messageVariables={[]}
docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart}
/>
);
expect(wrapper.find('[data-test-subj="urgencySelect"]').length > 0).toBeTruthy();

View file

@ -12,7 +12,7 @@ import { SlackActionConnector } from '../types';
const SlackActionFields: React.FunctionComponent<ActionConnectorFieldsProps<
SlackActionConnector
>> = ({ action, editActionSecrets, errors }) => {
>> = ({ action, editActionSecrets, errors, docLinks }) => {
const { webhookUrl } = action.secrets;
return (
@ -22,7 +22,7 @@ const SlackActionFields: React.FunctionComponent<ActionConnectorFieldsProps<
fullWidth
helpText={
<EuiLink
href="https://www.elastic.co/guide/en/elasticsearch/reference/current/actions-slack.html#configuring-slack"
href={`${docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${docLinks.DOC_LINK_VERSION}/slack-action-type.html#configuring-slack`}
target="_blank"
>
<FormattedMessage

View file

@ -6,6 +6,7 @@
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import SlackParamsFields from './slack_params';
import { DocLinksStart } from 'kibana/public';
describe('SlackParamsFields renders', () => {
test('all params fields is rendered', () => {
@ -18,6 +19,7 @@ describe('SlackParamsFields renders', () => {
errors={{ message: [] }}
editAction={() => {}}
index={0}
docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart}
/>
);
expect(wrapper.find('[data-test-subj="messageTextArea"]').length > 0).toBeTruthy();

View file

@ -6,6 +6,7 @@
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import WebhookParamsFields from './webhook_params';
import { DocLinksStart } from 'kibana/public';
describe('WebhookParamsFields renders', () => {
test('all params fields is rendered', () => {
@ -18,6 +19,7 @@ describe('WebhookParamsFields renders', () => {
errors={{ body: [] }}
editAction={() => {}}
index={0}
docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart}
/>
);
expect(wrapper.find('[data-test-subj="bodyJsonEditor"]').length > 0).toBeTruthy();

View file

@ -18,6 +18,7 @@ interface Props {
errors?: string[];
areaLabel?: string;
onDocumentsChange: (data: string) => void;
helpText?: JSX.Element;
}
export const JsonEditorWithMessageVariables: React.FunctionComponent<Props> = ({
@ -28,6 +29,7 @@ export const JsonEditorWithMessageVariables: React.FunctionComponent<Props> = ({
errors,
areaLabel,
onDocumentsChange,
helpText,
}) => {
const [cursorPosition, setCursorPosition] = useState<any>(null);
@ -65,6 +67,7 @@ export const JsonEditorWithMessageVariables: React.FunctionComponent<Props> = ({
paramsProperty={paramsProperty}
/>
}
helpText={helpText}
>
<EuiCodeEditor
mode={xJsonMode}

View file

@ -138,7 +138,6 @@ export const ActionConnectorForm = ({
>
<EuiFieldText
fullWidth
autoFocus={true}
isInvalid={errors.name.length > 0 && connector.name !== undefined}
name="name"
placeholder="Untitled"

View file

@ -313,6 +313,7 @@ export const ActionForm = ({
editAction={setActionParamsProperty}
messageVariables={messageVariables}
defaultMessage={defaultActionMessage ?? undefined}
docLinks={docLinks}
/>
</Suspense>
) : null}

View file

@ -42,6 +42,7 @@ export interface ActionParamsProps<TParams> {
errors: IErrorObject;
messageVariables?: string[];
defaultMessage?: string;
docLinks: DocLinksStart;
}
export interface Pagination {