-
Notifications
You must be signed in to change notification settings - Fork 11
Issue 61 : Frontend - Add details in the datasource card #130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
| import React, { useState, useEffect, FormEvent } from 'react'; | ||
| import { ConfigSelect, ConnectionConfig, InlineInput } from '@grafana/aws-sdk'; | ||
| import { DataSourcePluginOptionsEditorProps, SelectableValue } from '@grafana/data'; | ||
| import { getBackendSrv } from '@grafana/runtime'; | ||
| import React, { FormEvent, useEffect, useState } from 'react'; | ||
| import { selectors } from 'selectors'; | ||
|
|
||
| import { | ||
| RedshiftDataSourceOptions, | ||
| RedshiftDataSourceSecureJsonData, | ||
| RedshiftDataSourceSettings, | ||
| RedshiftManagedSecret, | ||
| } from '../types'; | ||
| import { InlineInput, ConfigSelect, ConnectionConfig } from '@grafana/aws-sdk'; | ||
| import { getBackendSrv } from '@grafana/runtime'; | ||
| import { AuthTypeSwitch } from './AuthTypeSwitch'; | ||
| import { selectors } from 'selectors'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
auto re-org
src/ConfigEditor/ConfigEditor.tsx
Outdated
| return res.map((c) => ({ | ||
| label: c.clusterIdentifier, | ||
| value: c.clusterIdentifier, | ||
| description: `${c.endpoint.address}:${c.endpoint.port}/${c.database}`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this returns a database, should we make the input disable and fill automatically based on clusterId selected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should, that's the default database, it's possible to create a different one in the same cluster and use that one instead. I would also not display this URL in this selector as the description for the same reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
So I should use the database entered in the db field to create the url in the datasource card instead of this default one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, that would make sense
andresmgot
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Just a couple of minor comments here.
Also, would you able to create a PR for the SDK allowing to set other properties? (context) We can open a different issue for that if you don't have time now to do it atm.
src/ConfigEditor/ConfigEditor.tsx
Outdated
| return res.map((c) => ({ | ||
| label: c.clusterIdentifier, | ||
| value: c.clusterIdentifier, | ||
| description: `${c.endpoint.address}:${c.endpoint.port}/${c.database}`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should, that's the default database, it's possible to create a different one in the same cluster and use that one instead. I would also not display this URL in this selector as the description for the same reason.
| data-testid={selectors.components.ConfigEditor.ClusterID.testID} | ||
| disabled={useManagedSecret} | ||
| saveOptions={saveOptions} | ||
| hidden={useManagedSecret} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should still show this field as disabled when useManagedSecret
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
weird, when I have disabled={useManagedSecret} it does not disable the select.
When I inspect, I see that although ConfigSelect has disabled=true it's child ResourceSelector has disabled=false.
It seems to be because we override disable there: https://github.com/grafana/grafana-aws-sdk-react/blob/6a051089daf5c577297f4bf695909b4da0194f9f/src/sql/ConfigEditor/ConfigSelect.tsx#L47
I will fix that as part of the SDK PR 🙂
|
BTW, you need to adapt the e2e test (smoke.test.ts) to this change. See how it's done for |
andresmgot
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! but it would be better if we fix the bugs in the SDK before merging this PR (so main is still releaseable, just in case)
cypress/integration/smoke.spec.ts
Outdated
| .click({ force: true }) | ||
| .type(datasource.jsonData.defaultRegion) | ||
| .type('{enter}'); | ||
| e2eSelectors.ConfigEditor.ClusterID.testID() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you need to click first, wait for the clusters to load and then type here (see lines 113:115 for the example with secrets)
Agreed! |
| expect(screen.getByText(selectors.components.ConfigEditor.ManagedSecret.input)).not.toBeVisible(); | ||
| expect(screen.getByTestId(selectors.components.ConfigEditor.ClusterIDText.testID)).not.toBeVisible(); | ||
| expect(screen.getByTestId(selectors.components.ConfigEditor.ClusterID.testID)).toBeVisible(); | ||
| expect(screen.getByTestId(selectors.components.ConfigEditor.ClusterID.testID)).not.toBeDisabled(); | ||
| expect(screen.getByText(selectors.components.ConfigEditor.DatabaseUser.input)).not.toBeDisabled(); | ||
| expect(screen.getByText(selectors.components.ConfigEditor.Database.input)).not.toBeDisabled(); | ||
| }); | ||
|
|
||
| it('should switch to use the Secret Manager', () => { | ||
| render(<ConfigEditor {...props} />); | ||
| screen.getByText('AWS Secrets Manager').click(); | ||
| expect(screen.getByText(selectors.components.ConfigEditor.ManagedSecret.input)).toBeInTheDocument(); | ||
| expect(screen.getByText(selectors.components.ConfigEditor.ClusterID.input)).toBeInTheDocument(); | ||
| expect(screen.getByText(selectors.components.ConfigEditor.Database.input)).toBeInTheDocument(); | ||
| expect(screen.getByText(selectors.components.ConfigEditor.ManagedSecret.input)).toBeVisible(); | ||
| expect(screen.getByTestId(selectors.components.ConfigEditor.ClusterIDText.testID)).toBeVisible(); | ||
| expect(screen.getByTestId(selectors.components.ConfigEditor.ClusterIDText.testID)).toBeDisabled(); | ||
| expect(screen.getByTestId(selectors.components.ConfigEditor.ClusterID.testID)).not.toBeVisible(); | ||
| expect(screen.getByTestId(selectors.components.ConfigEditor.DatabaseUser.testID)).toBeDisabled(); | ||
| expect(screen.getByText(selectors.components.ConfigEditor.Database.input)).not.toBeDisabled(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tests would always pass as even hidden or disabled components will be in the document 😄
I've made them stricter to check which inputs are disabled and which are visible
| const getClusterUrl = async (clusterID: string) => { | ||
| const { jsonData } = props.options; | ||
| if (clusterID != jsonData.clusterIdentifier) { | ||
| const clusters = await fetchClusters(); | ||
| return `${clusters.find((c) => c.value === clusterID)?.description || clusterID}/${jsonData.database || ''}`; | ||
| } | ||
| return props.options.url; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to use memoize here to avoid fetching unnecessary clusters (if clusterID is still the same) but I could not get it to work so I added the memo check myself 😉
| hidden={!useManagedSecret} | ||
| /> | ||
| <InlineInput | ||
| <ConfigSelect |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A dropdown for better use experience in temporary Creds
| saveOptions={saveOptions} | ||
| hidden={useManagedSecret} | ||
| /> | ||
| <InlineInput |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A text input to display clusterID for managed Secret
cypress/integration/smoke.spec.ts
Outdated
| e2eSelectors.ConfigEditor.ManagedSecret.input().click({ force: true }); | ||
| // wait for it to load | ||
| e2eSelectors.ConfigEditor.ManagedSecret.testID().contains(datasource.jsonData.managedSecret.name); | ||
| // e2eSelectors.ConfigEditor.ManagedSecret.testID().contains(datasource.jsonData.managedSecret.name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any idea why this line fails? 🤔
I figured we don't need it as the next few ones types in the expected secret and selects it but leaving as commented out for visibility
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mmh, it should work, if not we are risking that the options loads after start typing the name so it messes it up. Are you able to reproduce the error locally? If not it could be the case that the CI account doesn't have enough permissions after the last changes(?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's managedSecret, not clusters so permissions should not be an issue 🤷♀️
I can replicate locally, it is set up exactly the same way as the clusterID dropdown (which passes) - some puzzling behaviour! 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mmh, weird, anyway it seems stable now
andresmgot
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some comments but mostly minor. Good job!
| <InlineInput | ||
| {...props} | ||
| value={props.options.jsonData.clusterIdentifier ?? ''} | ||
| onChange={() => {}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hehe we should make this property optional (not needed now)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added issue: grafana/grafana-aws-sdk-react#15
src/ConfigEditor/ConfigEditor.tsx
Outdated
| onChange={() => {}} | ||
| label={selectors.components.ConfigEditor.ClusterIDText.input} | ||
| data-testid={selectors.components.ConfigEditor.ClusterIDText.testID} | ||
| disabled={useManagedSecret} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: disabled={true} since it will always be disabled
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well spotted 😄
| expect(onChange).toHaveBeenCalledTimes(1); | ||
| expect(onChange).toHaveBeenCalledWith({ | ||
| ...props.options, | ||
| jsonData: { ...props.options.jsonData, database: 'abcd' }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you also cover the case in which the url is set here? I believe it's not modifying it in this test because the url was empty before
| clusterIdentifier: s.dbClusterIdentifier, | ||
| dbUser: s.username, | ||
| }, | ||
| getClusterUrl(s.dbClusterIdentifier).then((url) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can add a ".catch" for the getClusterUrl so even if the user doesn't have permissions to get clusters, we set the identifier and the db user as we did before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added inside fetchClusters to return an empty array in catch
src/ConfigEditor/ConfigEditor.tsx
Outdated
| }; | ||
| const onChange = (resource: InputResourceType) => (e: FormEvent<HTMLInputElement>) => { | ||
| const value = e.currentTarget.value; | ||
| const url = resource === 'database' ? props.options.url.replace(/\/.*$/, `/${value}`) : props.options.url; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this regex is a bit risky because it assumes that the URL will contain one and only one /. One option would be to store the current cluster URL (without the database) in the state, so this URL can be re-calculated on every change but up to you since it's not that critical.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I agree, I could not find a workaround but using state seems like a good idea, I'll try that
cypress/integration/smoke.spec.ts
Outdated
| e2eSelectors.ConfigEditor.ManagedSecret.input().click({ force: true }); | ||
| // wait for it to load | ||
| e2eSelectors.ConfigEditor.ManagedSecret.testID().contains(datasource.jsonData.managedSecret.name); | ||
| // e2eSelectors.ConfigEditor.ManagedSecret.testID().contains(datasource.jsonData.managedSecret.name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mmh, it should work, if not we are risking that the options loads after start typing the name so it messes it up. Are you able to reproduce the error locally? If not it could be the case that the CI account doesn't have enough permissions after the last changes(?).
andresmgot
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 👍
Fixes #61
Description
When listing datasources, a card is composed of a Heading ('Amazon Redshift'), a Figure (the logo) and some
metainfo. Meta include the datasourcetypeName,urlandisDefault.This PR uses the endpoint created by #129 to use a dropdown instead of an input field and populate the
urlfield.It also upgrades
aws-sdkto useallowCustomValuein case the user does not have permission to fetch the clustersOn the
managed Secrettab, the dropdown is hidden and a plain text field is shown. The reason for that is that since the dropdown gets its options populated dynamically, when selecting a secret it would not show the clusterID associated with it (as it's a disabled dropdown with no valid options)Screenshots
No change to the
managed secretUIDropdown instead of text input for
Temp credentialUIDetails added to card in datasources list view
AWS Secret Managerview works as expectedTest coverage
Running:
yarn test:coverage:change