Skip to content

Commit

Permalink
OpenAI Preconfigured Connector Dashboard Link (elastic#169148)
Browse files Browse the repository at this point in the history
(cherry picked from commit 75e9d46)
  • Loading branch information
stephmilovic committed Oct 24, 2023
1 parent 89f1a28 commit dfa732e
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,21 @@
* 2.0.
*/

import React, { useCallback, useMemo } from 'react';
import React, { useMemo } from 'react';
import {
ActionConnectorFieldsProps,
SimpleConnectorForm,
} from '@kbn/triggers-actions-ui-plugin/public';
import { SelectField } from '@kbn/es-ui-shared-plugin/static/forms/components';
import { EuiLink, EuiSpacer } from '@elastic/eui';
import { EuiSpacer } from '@elastic/eui';
import {
UseField,
useFormContext,
useFormData,
} from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import { useKibana } from '@kbn/triggers-actions-ui-plugin/public';
import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
import DashboardLink from './dashboard_link';
import { OpenAiProviderType } from '../../../common/openai/constants';
import { useGetDashboard } from './use_get_dashboard';
import * as i18n from './translations';
import {
azureAiConfig,
Expand All @@ -37,24 +36,6 @@ const ConnectorFields: React.FC<ActionConnectorFieldsProps> = ({ readOnly, isEdi
watch: ['config.apiProvider'],
});

const {
services: {
application: { navigateToUrl },
},
} = useKibana();

const { dashboardUrl } = useGetDashboard({ connectorId: id });

const onClick = useCallback(
(e) => {
e.preventDefault();
if (dashboardUrl) {
navigateToUrl(dashboardUrl);
}
},
[dashboardUrl, navigateToUrl]
);

const selectedProviderDefaultValue = useMemo(
() =>
getFieldDefaultValue<OpenAiProviderType>('config.apiProvider') ?? OpenAiProviderType.OpenAi,
Expand Down Expand Up @@ -104,10 +85,12 @@ const ConnectorFields: React.FC<ActionConnectorFieldsProps> = ({ readOnly, isEdi
secretsFormSchema={azureAiSecrets}
/>
)}
{isEdit && dashboardUrl != null && (
<EuiLink data-test-subj="link-gen-ai-token-dashboard" onClick={onClick}>
{i18n.USAGE_DASHBOARD_LINK(selectedProviderDefaultValue, name)}
</EuiLink>
{isEdit && (
<DashboardLink
connectorId={id}
connectorName={name}
selectedProvider={selectedProviderDefaultValue}
/>
)}
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React, { useCallback } from 'react';
import { EuiLink } from '@elastic/eui';
import { useKibana } from '@kbn/triggers-actions-ui-plugin/public';
import * as i18n from './translations';
import { useGetDashboard } from './use_get_dashboard';

interface Props {
connectorId: string;
connectorName: string;
selectedProvider?: string;
}
// tested from ./connector.test.tsx
export const DashboardLink: React.FC<Props> = ({
connectorId,
connectorName,
selectedProvider = '',
}) => {
const { dashboardUrl } = useGetDashboard({ connectorId });
const {
services: {
application: { navigateToUrl },
},
} = useKibana();
const onClick = useCallback(
(e) => {
e.preventDefault();
if (dashboardUrl) {
navigateToUrl(dashboardUrl);
}
},
[dashboardUrl, navigateToUrl]
);
return dashboardUrl != null ? (
<EuiLink data-test-subj="link-gen-ai-token-dashboard" onClick={onClick}>
{i18n.USAGE_DASHBOARD_LINK(selectedProvider, connectorName)}
</EuiLink>
) : null;
};

// eslint-disable-next-line import/no-default-export
export { DashboardLink as default };
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,6 @@ export function getConnectorType(): OpenAIConnector {
},
actionConnectorFields: lazy(() => import('./connector')),
actionParamsFields: lazy(() => import('./params')),
actionReadOnlyExtraComponent: lazy(() => import('./dashboard_link')),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,12 @@
*/

import React, { memo, ReactNode, useCallback, useEffect, useRef, useState } from 'react';
import {
EuiFlyout,
EuiText,
EuiFlyoutBody,
EuiLink,
EuiButton,
EuiConfirmModal,
} from '@elastic/eui';
import { EuiFlyout, EuiFlyoutBody, EuiButton, EuiConfirmModal } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { ActionTypeExecutorResult, isActionTypeExecutorResult } from '@kbn/actions-plugin/common';
import { Option, none, some } from 'fp-ts/lib/Option';
import { ReadOnlyConnectorMessage } from './read_only';
import {
ActionConnector,
ActionTypeModel,
Expand Down Expand Up @@ -51,24 +45,6 @@ const getConnectorWithoutSecrets = (
secrets: {},
});

const ReadOnlyConnectorMessage: React.FC<{ href: string }> = ({ href }) => {
return (
<>
<EuiText>
{i18n.translate('xpack.triggersActionsUI.sections.editConnectorForm.descriptionText', {
defaultMessage: 'This connector is readonly.',
})}
</EuiText>
<EuiLink href={href} target="_blank">
<FormattedMessage
id="xpack.triggersActionsUI.sections.editConnectorForm.preconfiguredHelpLabel"
defaultMessage="Learn more about preconfigured connectors."
/>
</EuiLink>
</>
);
};

const EditConnectorFlyoutComponent: React.FC<EditConnectorFlyoutProps> = ({
actionTypeRegistry,
connector,
Expand Down Expand Up @@ -299,7 +275,12 @@ const EditConnectorFlyoutComponent: React.FC<EditConnectorFlyoutProps> = ({
)}
</>
) : (
<ReadOnlyConnectorMessage href={docLinks.links.alerting.preconfiguredConnectors} />
<ReadOnlyConnectorMessage
href={docLinks.links.alerting.preconfiguredConnectors}
extraComponent={actionTypeModel?.actionReadOnlyExtraComponent}
connectorId={connector.id}
connectorName={connector.name}
/>
)
) : (
<TestConnectorForm
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { I18nProvider } from '@kbn/i18n-react';
import { render } from '@testing-library/react';
import { ReadOnlyConnectorMessage } from './read_only';
import { ActionTypeModel } from '../../../..';

const ExtraComponent = jest.fn(() => (
<div>Extra Component</div>
)) as unknown as ActionTypeModel['actionReadOnlyExtraComponent'];
describe('ReadOnlyConnectorMessage', () => {
it('should render a readonly message with a link to the provided href', () => {
const { getByTestId, getByText, queryByText } = render(
<ReadOnlyConnectorMessage
connectorId="123"
connectorName="Test Connector"
href="https://example.com/"
/>,
{ wrapper: I18nProvider }
);

expect(getByText('This connector is readonly.')).toBeInTheDocument();
expect(getByTestId('read-only-link')).toHaveProperty('href', 'https://example.com/');
expect(queryByText('Extra Component')).toBeNull();
});

it('should render an extra component if provided', () => {
const { getByText } = render(
<ReadOnlyConnectorMessage
connectorId="123"
connectorName="Test Connector"
extraComponent={ExtraComponent}
href="https://example.com"
/>,
{ wrapper: I18nProvider }
);

expect(getByText('Extra Component')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { EuiLink, EuiSpacer, EuiText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { ActionTypeModel } from '../../../..';

export const ReadOnlyConnectorMessage: React.FC<{
connectorId: string;
connectorName: string;
extraComponent?: ActionTypeModel['actionReadOnlyExtraComponent'];
href: string;
}> = ({ connectorId, connectorName, extraComponent, href }) => {
const ExtraComponent = extraComponent;
return (
<>
<EuiText>
{i18n.translate('xpack.triggersActionsUI.sections.editConnectorForm.descriptionText', {
defaultMessage: 'This connector is readonly.',
})}
</EuiText>
<EuiLink data-test-subj="read-only-link" href={href} target="_blank">
<FormattedMessage
id="xpack.triggersActionsUI.sections.editConnectorForm.preconfiguredHelpLabel"
defaultMessage="Learn more about preconfigured connectors."
/>
</EuiLink>
{ExtraComponent && (
<>
<EuiSpacer size="m" />
<ExtraComponent connectorId={connectorId} connectorName={connectorName} />
</>
)}
</>
);
};
7 changes: 7 additions & 0 deletions x-pack/plugins/triggers_actions_ui/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ export interface ActionConnectorFieldsProps {
isEdit: boolean;
registerPreSubmitValidator: (validator: ConnectorValidationFunc) => void;
}
export interface ActionReadOnlyElementProps {
connectorId: string;
connectorName: string;
}

export enum RuleFlyoutCloseReason {
SAVED,
Expand Down Expand Up @@ -254,6 +258,9 @@ export interface ActionTypeModel<ActionConfig = any, ActionSecrets = any, Action
ComponentType<ActionConnectorFieldsProps>
> | null;
actionParamsFields: React.LazyExoticComponent<ComponentType<ActionParamsProps<ActionParams>>>;
actionReadOnlyExtraComponent?: React.LazyExoticComponent<
ComponentType<ActionReadOnlyElementProps>
>;
defaultActionParams?: RecursivePartial<ActionParams>;
defaultRecoveredActionParams?: RecursivePartial<ActionParams>;
customConnectorSelectItem?: CustomConnectorSelectionItem;
Expand Down

0 comments on commit dfa732e

Please sign in to comment.