publicdashboards: split create/update api paths (#57940)

This PR splits the create and update paths for public dashboards and includes assorted refactors toward a proper REST API. Additionally, we removed the concept of a "public dashboard config" in favor of "public dashboard" 

Co-authored-by: juanicabanas <juan.cabanas@grafana.com>
Co-authored-by: Ezequiel Victorero <ezequiel.victorero@grafana.com>
This commit is contained in:
Jeff Levin
2022-11-03 11:30:12 -08:00
committed by GitHub
parent 0367f61bb3
commit 6fcc5b42c0
24 changed files with 996 additions and 612 deletions

View File

@@ -17,20 +17,7 @@ import { configureStore } from 'app/store/configureStore';
import { ShareModal } from '../ShareModal';
const server = setupServer(
rest.get('/api/dashboards/uid/:dashboardUid/public-dashboards', (_, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
isEnabled: false,
annotationsEnabled: false,
uid: undefined,
dashboardUid: undefined,
accessToken: 'an-access-token',
})
);
})
);
const server = setupServer();
jest.mock('@grafana/runtime', () => ({
...(jest.requireActual('@grafana/runtime') as unknown as object),
@@ -147,6 +134,7 @@ describe('SharePublic', () => {
expect(screen.getByText('2022-08-30 00:00:00 to 2022-09-04 01:59:59')).toBeInTheDocument();
});
it('when modal is opened, then loader spinner appears and inputs are disabled', async () => {
mockDashboard.meta.hasPublicDashboard = true;
await renderSharePublicDashboard({ panel: mockPanel, dashboard: mockDashboard, onDismiss: () => {} });
expect(await screen.findByTestId('Spinner')).toBeInTheDocument();
@@ -158,6 +146,7 @@ describe('SharePublic', () => {
expect(screen.getByTestId(selectors.SaveConfigButton)).toBeDisabled();
});
it('when fetch errors happen, then all inputs remain disabled', async () => {
mockDashboard.meta.hasPublicDashboard = true;
server.use(
rest.get('/api/dashboards/uid/:dashboardUid/public-dashboards', (req, res, ctx) => {
return res(ctx.status(500));
@@ -165,7 +154,7 @@ describe('SharePublic', () => {
);
await renderSharePublicDashboard({ panel: mockPanel, dashboard: mockDashboard, onDismiss: () => {} });
await waitForElementToBeRemoved(screen.getByTestId('Spinner'), { timeout: 7000 });
await waitForElementToBeRemoved(screen.getByTestId('Spinner'));
expect(screen.getByTestId(selectors.WillBePublicCheckbox)).toBeDisabled();
expect(screen.getByTestId(selectors.LimitedDSCheckbox)).toBeDisabled();
@@ -178,13 +167,16 @@ describe('SharePublic', () => {
});
describe('SharePublic - New config setup', () => {
beforeEach(() => {
mockDashboard.meta.hasPublicDashboard = false;
});
it('when modal is opened, then save button is disabled', async () => {
await renderSharePublicDashboard({ panel: mockPanel, dashboard: mockDashboard, onDismiss: () => {} });
expect(screen.getByTestId(selectors.SaveConfigButton)).toBeDisabled();
});
it('when fetch is done, then loader spinner is gone, inputs are enabled and save button is disabled', async () => {
it('when fetch is done, then no loader spinner appears, inputs are enabled and save button is disabled', async () => {
await renderSharePublicDashboard({ panel: mockPanel, dashboard: mockDashboard, onDismiss: () => {} });
await waitForElementToBeRemoved(screen.getByTestId('Spinner'));
expect(screen.queryByTestId('Spinner')).not.toBeInTheDocument();
expect(screen.getByTestId(selectors.WillBePublicCheckbox)).toBeEnabled();
expect(screen.getByTestId(selectors.LimitedDSCheckbox)).toBeEnabled();
@@ -196,7 +188,7 @@ describe('SharePublic - New config setup', () => {
});
it('when checkboxes are filled, then save button remains disabled', async () => {
await renderSharePublicDashboard({ panel: mockPanel, dashboard: mockDashboard, onDismiss: () => {} });
await waitForElementToBeRemoved(screen.getByTestId('Spinner'));
expect(screen.queryByTestId('Spinner')).not.toBeInTheDocument();
fireEvent.click(screen.getByTestId(selectors.WillBePublicCheckbox));
fireEvent.click(screen.getByTestId(selectors.LimitedDSCheckbox));
@@ -206,7 +198,7 @@ describe('SharePublic - New config setup', () => {
});
it('when checkboxes and switch are filled, then save button is enabled', async () => {
await renderSharePublicDashboard({ panel: mockPanel, dashboard: mockDashboard, onDismiss: () => {} });
await waitForElementToBeRemoved(screen.getByTestId('Spinner'));
expect(screen.queryByTestId('Spinner')).not.toBeInTheDocument();
fireEvent.click(screen.getByTestId(selectors.WillBePublicCheckbox));
fireEvent.click(screen.getByTestId(selectors.LimitedDSCheckbox));
@@ -219,6 +211,7 @@ describe('SharePublic - New config setup', () => {
describe('SharePublic - Already persisted', () => {
beforeEach(() => {
mockDashboard.meta.hasPublicDashboard = true;
server.use(
rest.get('/api/dashboards/uid/:dashboardUid/public-dashboards', (req, res, ctx) => {
return res(

View File

@@ -6,7 +6,11 @@ import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
import { reportInteraction } from '@grafana/runtime/src';
import { Alert, Button, ClipboardButton, Field, HorizontalGroup, Input, useStyles2, Spinner } from '@grafana/ui/src';
import { contextSrv } from 'app/core/services/context_srv';
import { useGetConfigQuery, useSaveConfigMutation } from 'app/features/dashboard/api/publicDashboardApi';
import {
useGetPublicDashboardQuery,
useCreatePublicDashboardMutation,
useUpdatePublicDashboardMutation,
} from 'app/features/dashboard/api/publicDashboardApi';
import { AcknowledgeCheckboxes } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/AcknowledgeCheckboxes';
import { Configuration } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/Configuration';
import { Description } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/Description';
@@ -27,13 +31,19 @@ export const SharePublicDashboard = (props: Props) => {
const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard;
const styles = useStyles2(getStyles);
const [hasPublicDashboard, setHasPublicDashboard] = useState(props.dashboard.meta.hasPublicDashboard);
const {
isLoading: isFetchingLoading,
data: publicDashboard,
isError: isFetchingError,
} = useGetConfigQuery(props.dashboard.uid);
} = useGetPublicDashboardQuery(props.dashboard.uid, {
// if we don't have a public dashboard, don't try to load public dashboard
skip: !hasPublicDashboard,
});
const [saveConfig, { isLoading: isSaveLoading }] = useSaveConfigMutation();
const [createPublicDashboard, { isLoading: isSaveLoading }] = useCreatePublicDashboardMutation();
const [updatePublicDashboard, { isLoading: isUpdateLoading }] = useUpdatePublicDashboardMutation();
const [acknowledgements, setAcknowledgements] = useState<Acknowledgements>({
public: false,
@@ -63,7 +73,7 @@ export const SharePublicDashboard = (props: Props) => {
setEnabledSwitch((prevState) => ({ ...prevState, isEnabled: !!publicDashboard?.isEnabled }));
}, [publicDashboard]);
const isLoading = isFetchingLoading || isSaveLoading;
const isLoading = isFetchingLoading || isSaveLoading || isUpdateLoading;
const hasWritePermissions = contextSrv.hasAccess(AccessControlAction.DashboardsPublicWrite, isOrgAdmin());
const acknowledged = acknowledgements.public && acknowledgements.datasources && acknowledgements.usage;
const isSaveEnabled = useMemo(
@@ -77,13 +87,23 @@ export const SharePublicDashboard = (props: Props) => {
[hasWritePermissions, acknowledged, props.dashboard, isLoading, isFetchingError, enabledSwitch, publicDashboard]
);
const onSavePublicConfig = () => {
const onSavePublicConfig = async () => {
reportInteraction('grafana_dashboards_public_create_clicked');
saveConfig({
const req = {
dashboard: props.dashboard,
payload: { ...publicDashboard!, isEnabled: enabledSwitch.isEnabled, annotationsEnabled },
});
};
// create or update based on whether we have existing uid
if (hasPublicDashboard) {
await updatePublicDashboard(req).unwrap();
setHasPublicDashboard(true);
} else {
await createPublicDashboard(req).unwrap();
setHasPublicDashboard(true);
}
};
const onAcknowledge = (field: string, checked: boolean) => {