CloudWatch: Remove core imports from ConfigEditor (#80837)

* CloudWatch: Remove core imports from ConfigEditor

* test
This commit is contained in:
Kevin Yu 2024-02-06 10:44:42 -08:00 committed by GitHub
parent 484106eb04
commit 0d3cf9a08b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 91 additions and 32 deletions

View File

@ -1,19 +1,26 @@
import { render, screen, waitFor } from '@testing-library/react'; import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import React from 'react'; import React from 'react';
import { Provider } from 'react-redux';
import { AwsAuthType } from '@grafana/aws-sdk'; import { AwsAuthType } from '@grafana/aws-sdk';
import { PluginContextProvider, PluginMeta, PluginMetaInfo, PluginType } from '@grafana/data'; import { PluginContextProvider, PluginMeta, PluginMetaInfo, PluginType } from '@grafana/data';
import { config } from '@grafana/runtime'; import { config } from '@grafana/runtime';
import { configureStore } from 'app/store/configureStore';
import { CloudWatchSettings, setupMockedDataSource } from '../../__mocks__/CloudWatchDataSource'; import {
CloudWatchSettings,
setupMockedDataSource,
setupMockedTemplateService,
} from '../../__mocks__/CloudWatchDataSource';
import { CloudWatchDatasource } from '../../datasource'; import { CloudWatchDatasource } from '../../datasource';
import { ConfigEditor, Props } from './ConfigEditor'; import {
ConfigEditor,
Props,
ARN_DEPRECATION_WARNING_MESSAGE,
CREDENTIALS_AUTHENTICATION_WARNING_MESSAGE,
} from './ConfigEditor';
const datasource = new CloudWatchDatasource(CloudWatchSettings); const datasource = new CloudWatchDatasource(CloudWatchSettings, setupMockedTemplateService());
const loadDataSourceMock = jest.fn(); const loadDataSourceMock = jest.fn();
jest.mock('./XrayLinkConfig', () => ({ jest.mock('./XrayLinkConfig', () => ({
@ -85,7 +92,6 @@ const props: Props = {
}; };
const setup = (optionOverrides?: Partial<Props['options']>) => { const setup = (optionOverrides?: Partial<Props['options']>) => {
const store = configureStore();
const newProps = { const newProps = {
...props, ...props,
options: { options: {
@ -104,9 +110,7 @@ const setup = (optionOverrides?: Partial<Props['options']>) => {
return render( return render(
<PluginContextProvider meta={meta}> <PluginContextProvider meta={meta}>
<Provider store={store}> <ConfigEditor {...newProps} />
<ConfigEditor {...newProps} />
</Provider>
</PluginContextProvider> </PluginContextProvider>
); );
}; };
@ -152,6 +156,45 @@ describe('Render', () => {
await waitFor(async () => expect(screen.getByText('Credentials Profile Name')).toBeInTheDocument()); await waitFor(async () => expect(screen.getByText('Credentials Profile Name')).toBeInTheDocument());
}); });
it('should show a warning if `credentials` auth type is used without a profile or database configured', async () => {
setup({
jsonData: {
authType: AwsAuthType.Credentials,
profile: undefined,
database: undefined,
},
});
await waitFor(async () =>
expect(screen.getByText(CREDENTIALS_AUTHENTICATION_WARNING_MESSAGE)).toBeInTheDocument()
);
});
it('should not show a warning if `credentials` auth type is used and a profile is configured', async () => {
setup({
jsonData: {
authType: AwsAuthType.Credentials,
profile: 'profile',
database: undefined,
},
});
await waitFor(async () =>
expect(screen.queryByText(CREDENTIALS_AUTHENTICATION_WARNING_MESSAGE)).not.toBeInTheDocument()
);
});
it('should not show a warning if `credentials` auth type is used and a database is configured', async () => {
setup({
jsonData: {
authType: AwsAuthType.Credentials,
profile: undefined,
database: 'database',
},
});
await waitFor(async () =>
expect(screen.queryByText(CREDENTIALS_AUTHENTICATION_WARNING_MESSAGE)).not.toBeInTheDocument()
);
});
it('should show access key and secret access key fields when the datasource has not been configured before', async () => { it('should show access key and secret access key fields when the datasource has not been configured before', async () => {
setup({ setup({
jsonData: { jsonData: {
@ -173,6 +216,15 @@ describe('Render', () => {
await waitFor(async () => expect(screen.getByText('Assume Role ARN')).toBeInTheDocument()); await waitFor(async () => expect(screen.getByText('Assume Role ARN')).toBeInTheDocument());
}); });
it('should show a deprecation warning if `arn` auth type is used', async () => {
setup({
jsonData: {
authType: AwsAuthType.ARN,
},
});
await waitFor(async () => expect(screen.getByText(ARN_DEPRECATION_WARNING_MESSAGE)).toBeInTheDocument());
});
it('should display log group selector field', async () => { it('should display log group selector field', async () => {
setup(); setup();
await waitFor(async () => expect(screen.getByText('Select log groups')).toBeInTheDocument()); await waitFor(async () => expect(screen.getByText('Select log groups')).toBeInTheDocument());

View File

@ -12,10 +12,7 @@ import {
} from '@grafana/data'; } from '@grafana/data';
import { ConfigSection } from '@grafana/experimental'; import { ConfigSection } from '@grafana/experimental';
import { getAppEvents, usePluginInteractionReporter, getDataSourceSrv, config } from '@grafana/runtime'; import { getAppEvents, usePluginInteractionReporter, getDataSourceSrv, config } from '@grafana/runtime';
import { Input, InlineField, FieldProps, SecureSocksProxySettings, Field, Divider } from '@grafana/ui'; import { Alert, Input, InlineField, FieldProps, SecureSocksProxySettings, Field, Divider } from '@grafana/ui';
import { notifyApp } from 'app/core/actions';
import { createWarningNotification } from 'app/core/copy/appNotification';
import { store } from 'app/store/store';
import { CloudWatchDatasource } from '../../datasource'; import { CloudWatchDatasource } from '../../datasource';
import { SelectableResourceValue } from '../../resources/types'; import { SelectableResourceValue } from '../../resources/types';
@ -29,11 +26,17 @@ export type Props = DataSourcePluginOptionsEditorProps<CloudWatchJsonData, Cloud
type LogGroupFieldState = Pick<FieldProps, 'invalid'> & { error?: string | null }; type LogGroupFieldState = Pick<FieldProps, 'invalid'> & { error?: string | null };
export const ARN_DEPRECATION_WARNING_MESSAGE =
'Since grafana 7.3 authentication type "arn" is deprecated, falling back to default SDK provider';
export const CREDENTIALS_AUTHENTICATION_WARNING_MESSAGE =
'As of grafana 7.3 authentication type "credentials" should be used only for shared file credentials. \
If you don\'t have a credentials file, switch to the default SDK provider for extracting credentials \
from environment variables or IAM roles';
export const ConfigEditor = (props: Props) => { export const ConfigEditor = (props: Props) => {
const { options, onOptionsChange } = props; const { options, onOptionsChange } = props;
const { defaultLogGroups, logsTimeout, defaultRegion, logGroups } = options.jsonData; const { defaultLogGroups, logsTimeout, defaultRegion, logGroups } = options.jsonData;
const datasource = useDatasource(props); const datasource = useDatasource(props);
useAuthenticationWarning(options.jsonData);
const logsTimeoutError = useTimoutValidation(logsTimeout); const logsTimeoutError = useTimoutValidation(logsTimeout);
const saved = useDataSourceSavedState(props); const saved = useDataSourceSavedState(props);
const [logGroupFieldState, setLogGroupFieldState] = useState<LogGroupFieldState>({ const [logGroupFieldState, setLogGroupFieldState] = useState<LogGroupFieldState>({
@ -68,8 +71,25 @@ export const ConfigEditor = (props: Props) => {
} }
}, [datasource, externalId]); }, [datasource, externalId]);
const [warning, setWarning] = useState<string | null>(null);
const dismissWarning = () => {
setWarning(null);
};
useEffect(() => {
if (options.jsonData.authType === 'arn') {
setWarning(ARN_DEPRECATION_WARNING_MESSAGE);
} else if (options.jsonData.authType === 'credentials' && !options.jsonData.profile && !options.jsonData.database) {
setWarning(CREDENTIALS_AUTHENTICATION_WARNING_MESSAGE);
}
}, [options.jsonData.authType, options.jsonData.database, options.jsonData.profile]);
return newFormStylingEnabled ? ( return newFormStylingEnabled ? (
<div className="width-30"> <div className="width-30">
{warning && (
<Alert title="CloudWatch Authentication" severity="warning" onRemove={dismissWarning}>
{warning}
</Alert>
)}
<ConnectionConfig <ConnectionConfig
{...props} {...props}
newFormStylingEnabled={true} newFormStylingEnabled={true}
@ -166,6 +186,11 @@ export const ConfigEditor = (props: Props) => {
</div> </div>
) : ( ) : (
<> <>
{warning && (
<Alert title="CloudWatch Authentication" severity="warning" onRemove={dismissWarning}>
{warning}
</Alert>
)}
<ConnectionConfig <ConnectionConfig
{...props} {...props}
labelWidth={29} labelWidth={29}
@ -272,24 +297,6 @@ export const ConfigEditor = (props: Props) => {
); );
}; };
function useAuthenticationWarning(jsonData: CloudWatchJsonData) {
const addWarning = (message: string) => {
store.dispatch(notifyApp(createWarningNotification('CloudWatch Authentication', message)));
};
useEffect(() => {
if (jsonData.authType === 'arn') {
addWarning('Since grafana 7.3 authentication type "arn" is deprecated, falling back to default SDK provider');
} else if (jsonData.authType === 'credentials' && !jsonData.profile && !jsonData.database) {
addWarning(
'As of grafana 7.3 authentication type "credentials" should be used only for shared file credentials. \
If you don\'t have a credentials file, switch to the default SDK provider for extracting credentials \
from environment variables or IAM roles'
);
}
}, [jsonData.authType, jsonData.database, jsonData.profile]);
}
function useDatasource(props: Props) { function useDatasource(props: Props) {
const [datasource, setDatasource] = useState<CloudWatchDatasource>(); const [datasource, setDatasource] = useState<CloudWatchDatasource>();