mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: remove dashboardsFromStorage (#65058)
This commit is contained in:
parent
fbb0dcb0ca
commit
91a4b8b529
@ -2876,11 +2876,6 @@ exports[`better eslint`] = {
|
|||||||
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "2"],
|
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "2"],
|
||||||
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "3"]
|
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "3"]
|
||||||
],
|
],
|
||||||
"public/app/features/dashboard/components/SaveDashboard/forms/SaveToStorageForm.tsx:5381": [
|
|
||||||
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"],
|
|
||||||
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "1"],
|
|
||||||
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "2"]
|
|
||||||
],
|
|
||||||
"public/app/features/dashboard/components/SaveDashboard/types.ts:5381": [
|
"public/app/features/dashboard/components/SaveDashboard/types.ts:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||||
|
@ -100,10 +100,9 @@ Alpha features might be changed or removed without prior notice.
|
|||||||
|
|
||||||
The following toggles require explicitly setting Grafana's [app mode]({{< relref "../_index.md/#app_mode" >}}) to 'development' before you can enable this feature toggle. These features tend to be experimental.
|
The following toggles require explicitly setting Grafana's [app mode]({{< relref "../_index.md/#app_mode" >}}) to 'development' before you can enable this feature toggle. These features tend to be experimental.
|
||||||
|
|
||||||
| Feature toggle name | Description |
|
| Feature toggle name | Description |
|
||||||
| ----------------------- | --------------------------------------------------- |
|
| ------------------- | --------------------------------------------------- |
|
||||||
| `k8s` | Explore native k8s integrations |
|
| `k8s` | Explore native k8s integrations |
|
||||||
| `dashboardsFromStorage` | Load dashboards from the generic storage interface |
|
| `grpcServer` | Run GRPC server |
|
||||||
| `grpcServer` | Run GRPC server |
|
| `entityStore` | SQL-based entity store (requires storage flag also) |
|
||||||
| `entityStore` | SQL-based entity store (requires storage flag also) |
|
| `nestedFolders` | Enable folder nesting |
|
||||||
| `nestedFolders` | Enable folder nesting |
|
|
||||||
|
@ -36,7 +36,6 @@ export interface FeatureToggles {
|
|||||||
migrationLocking?: boolean;
|
migrationLocking?: boolean;
|
||||||
storage?: boolean;
|
storage?: boolean;
|
||||||
k8s?: boolean;
|
k8s?: boolean;
|
||||||
dashboardsFromStorage?: boolean;
|
|
||||||
exploreMixedDatasource?: boolean;
|
exploreMixedDatasource?: boolean;
|
||||||
tracing?: boolean;
|
tracing?: boolean;
|
||||||
newTraceView?: boolean;
|
newTraceView?: boolean;
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/annotations"
|
"github.com/grafana/grafana/pkg/services/annotations"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
"github.com/grafana/grafana/pkg/services/guardian"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
@ -53,11 +52,7 @@ func (hs *HTTPServer) GetAnnotations(c *contextmodel.ReqContext) response.Respon
|
|||||||
dq := dashboards.GetDashboardQuery{UID: query.DashboardUID, OrgID: c.OrgID}
|
dq := dashboards.GetDashboardQuery{UID: query.DashboardUID, OrgID: c.OrgID}
|
||||||
dqResult, err := hs.DashboardService.GetDashboard(c.Req.Context(), &dq)
|
dqResult, err := hs.DashboardService.GetDashboard(c.Req.Context(), &dq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if hs.Features.IsEnabled(featuremgmt.FlagDashboardsFromStorage) {
|
return response.Error(http.StatusBadRequest, "Invalid dashboard UID in annotation request", err)
|
||||||
// OK... the storage UIDs do not (yet?) exist in the DashboardService
|
|
||||||
} else {
|
|
||||||
return response.Error(http.StatusBadRequest, "Invalid dashboard UID in annotation request", err)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
query.DashboardID = dqResult.ID
|
query.DashboardID = dqResult.ID
|
||||||
}
|
}
|
||||||
|
@ -157,10 +157,6 @@ func (hs *HTTPServer) registerRoutes() {
|
|||||||
r.Get("/dashboards/*", reqSignedIn, hs.Index)
|
r.Get("/dashboards/*", reqSignedIn, hs.Index)
|
||||||
r.Get("/goto/:uid", reqSignedIn, hs.redirectFromShortURL, hs.Index)
|
r.Get("/goto/:uid", reqSignedIn, hs.redirectFromShortURL, hs.Index)
|
||||||
|
|
||||||
if hs.Features.IsEnabled(featuremgmt.FlagDashboardsFromStorage) {
|
|
||||||
r.Get("/g/*", reqSignedIn, hs.Index)
|
|
||||||
}
|
|
||||||
|
|
||||||
if hs.Features.IsEnabled(featuremgmt.FlagPublicDashboards) {
|
if hs.Features.IsEnabled(featuremgmt.FlagPublicDashboards) {
|
||||||
// list public dashboards
|
// list public dashboards
|
||||||
r.Get("/public-dashboards/list", reqSignedIn, hs.Index)
|
r.Get("/public-dashboards/list", reqSignedIn, hs.Index)
|
||||||
|
@ -121,13 +121,6 @@ var (
|
|||||||
RequiresDevMode: true,
|
RequiresDevMode: true,
|
||||||
Owner: grafanaAppPlatformSquad,
|
Owner: grafanaAppPlatformSquad,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Name: "dashboardsFromStorage",
|
|
||||||
Description: "Load dashboards from the generic storage interface",
|
|
||||||
State: FeatureStateAlpha,
|
|
||||||
RequiresDevMode: true, // Also a gate on automatic git storage (for now)
|
|
||||||
Owner: grafanaAppPlatformSquad,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Name: "exploreMixedDatasource",
|
Name: "exploreMixedDatasource",
|
||||||
Description: "Enable mixed datasource in Explore",
|
Description: "Enable mixed datasource in Explore",
|
||||||
|
@ -17,7 +17,6 @@ featureHighlights,stable,@grafana/grafana-as-code,false,false,false,false
|
|||||||
migrationLocking,beta,@grafana/backend-platform,false,false,false,false
|
migrationLocking,beta,@grafana/backend-platform,false,false,false,false
|
||||||
storage,alpha,@grafana/grafana-app-platform-squad,false,false,false,false
|
storage,alpha,@grafana/grafana-app-platform-squad,false,false,false,false
|
||||||
k8s,alpha,@grafana/grafana-app-platform-squad,true,false,false,false
|
k8s,alpha,@grafana/grafana-app-platform-squad,true,false,false,false
|
||||||
dashboardsFromStorage,alpha,@grafana/grafana-app-platform-squad,true,false,false,false
|
|
||||||
exploreMixedDatasource,alpha,@grafana/explore-squad,false,false,false,true
|
exploreMixedDatasource,alpha,@grafana/explore-squad,false,false,false,true
|
||||||
tracing,alpha,@grafana/user-essentials,false,false,false,true
|
tracing,alpha,@grafana/user-essentials,false,false,false,true
|
||||||
newTraceView,alpha,@grafana/observability-traces-and-profiling,false,false,false,true
|
newTraceView,alpha,@grafana/observability-traces-and-profiling,false,false,false,true
|
||||||
|
|
@ -79,10 +79,6 @@ const (
|
|||||||
// Explore native k8s integrations
|
// Explore native k8s integrations
|
||||||
FlagK8S = "k8s"
|
FlagK8S = "k8s"
|
||||||
|
|
||||||
// FlagDashboardsFromStorage
|
|
||||||
// Load dashboards from the generic storage interface
|
|
||||||
FlagDashboardsFromStorage = "dashboardsFromStorage"
|
|
||||||
|
|
||||||
// FlagExploreMixedDatasource
|
// FlagExploreMixedDatasource
|
||||||
// Enable mixed datasource in Explore
|
// Enable mixed datasource in Explore
|
||||||
FlagExploreMixedDatasource = "exploreMixedDatasource"
|
FlagExploreMixedDatasource = "exploreMixedDatasource"
|
||||||
|
@ -44,34 +44,6 @@ func LoadStorageConfig(cfg *setting.Cfg, features featuremgmt.FeatureToggles) (*
|
|||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if g.Roots == nil && features.IsEnabled(featuremgmt.FlagDashboardsFromStorage) {
|
|
||||||
g.Roots = append(g.Roots, RootStorageConfig{
|
|
||||||
Type: "git",
|
|
||||||
Prefix: "it-A",
|
|
||||||
Name: "Repository that requires pull requests",
|
|
||||||
Git: &StorageGitConfig{
|
|
||||||
Remote: "https://github.com/grafana/hackathon-2022-03-git-dash-A",
|
|
||||||
Branch: "main",
|
|
||||||
Root: "dashboards", // the dashboard files
|
|
||||||
RequirePullRequest: true,
|
|
||||||
AccessToken: "$GRAFANA_STORAGE_GITHUB_ACCESS_TOKEN",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
g.Roots = append(g.Roots, RootStorageConfig{
|
|
||||||
Type: "git",
|
|
||||||
Prefix: "it-B",
|
|
||||||
Name: "Another repo (can push to main)",
|
|
||||||
Git: &StorageGitConfig{
|
|
||||||
Remote: "https://github.com/grafana/hackathon-2022-03-git-dash-B",
|
|
||||||
Branch: "main",
|
|
||||||
Root: "dashboards", // the dashboard files
|
|
||||||
RequirePullRequest: false,
|
|
||||||
AccessToken: "$GRAFANA_STORAGE_GITHUB_ACCESS_TOKEN",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
changed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
g.filepath = fpath
|
g.filepath = fpath
|
||||||
|
|
||||||
// Also configured from ini files
|
// Also configured from ini files
|
||||||
|
@ -20,7 +20,6 @@ import { getConfig } from 'app/core/config';
|
|||||||
import { loadUrlToken } from 'app/core/utils/urlToken';
|
import { loadUrlToken } from 'app/core/utils/urlToken';
|
||||||
import { DashboardModel } from 'app/features/dashboard/state';
|
import { DashboardModel } from 'app/features/dashboard/state';
|
||||||
import { DashboardSearchItem } from 'app/features/search/types';
|
import { DashboardSearchItem } from 'app/features/search/types';
|
||||||
import { getGrafanaStorage } from 'app/features/storage/storage';
|
|
||||||
import { TokenRevokedModal } from 'app/features/users/TokenRevokedModal';
|
import { TokenRevokedModal } from 'app/features/users/TokenRevokedModal';
|
||||||
import { DashboardDTO, FolderDTO } from 'app/types';
|
import { DashboardDTO, FolderDTO } from 'app/types';
|
||||||
|
|
||||||
@ -456,9 +455,6 @@ export class BackendSrv implements BackendService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getDashboardByUid(uid: string): Promise<DashboardDTO> {
|
getDashboardByUid(uid: string): Promise<DashboardDTO> {
|
||||||
if (uid.indexOf('/') > 0 && config.featureToggles.dashboardsFromStorage) {
|
|
||||||
return getGrafanaStorage().getDashboard(uid);
|
|
||||||
}
|
|
||||||
return this.get<DashboardDTO>(`/api/dashboards/uid/${uid}`);
|
return this.get<DashboardDTO>(`/api/dashboards/uid/${uid}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,16 +13,14 @@ import { SaveDashboardErrorProxy } from './SaveDashboardErrorProxy';
|
|||||||
import { SaveDashboardAsForm } from './forms/SaveDashboardAsForm';
|
import { SaveDashboardAsForm } from './forms/SaveDashboardAsForm';
|
||||||
import { SaveDashboardForm } from './forms/SaveDashboardForm';
|
import { SaveDashboardForm } from './forms/SaveDashboardForm';
|
||||||
import { SaveProvisionedDashboardForm } from './forms/SaveProvisionedDashboardForm';
|
import { SaveProvisionedDashboardForm } from './forms/SaveProvisionedDashboardForm';
|
||||||
import { SaveToStorageForm } from './forms/SaveToStorageForm';
|
|
||||||
import { SaveDashboardData, SaveDashboardModalProps, SaveDashboardOptions } from './types';
|
import { SaveDashboardData, SaveDashboardModalProps, SaveDashboardOptions } from './types';
|
||||||
import { useDashboardSave } from './useDashboardSave';
|
import { useDashboardSave } from './useDashboardSave';
|
||||||
|
|
||||||
export const SaveDashboardDrawer = ({ dashboard, onDismiss, onSaveSuccess, isCopy }: SaveDashboardModalProps) => {
|
export const SaveDashboardDrawer = ({ dashboard, onDismiss, onSaveSuccess, isCopy }: SaveDashboardModalProps) => {
|
||||||
const [options, setOptions] = useState<SaveDashboardOptions>({});
|
const [options, setOptions] = useState<SaveDashboardOptions>({});
|
||||||
|
|
||||||
const isFromStorage = config.featureToggles.dashboardsFromStorage && dashboard.uid?.indexOf('/') > 0;
|
const isProvisioned = dashboard.meta.provisioned;
|
||||||
const isProvisioned = dashboard.meta.provisioned && !isFromStorage;
|
const isNew = dashboard.version === 0;
|
||||||
const isNew = dashboard.version === 0 && !isFromStorage;
|
|
||||||
|
|
||||||
const previous = useAsync(async () => {
|
const previous = useAsync(async () => {
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
@ -82,22 +80,6 @@ export const SaveDashboardDrawer = ({ dashboard, onDismiss, onSaveSuccess, isCop
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFromStorage) {
|
|
||||||
return (
|
|
||||||
<SaveToStorageForm
|
|
||||||
dashboard={dashboard}
|
|
||||||
saveModel={data}
|
|
||||||
onCancel={onDismiss}
|
|
||||||
onSuccess={onSuccess}
|
|
||||||
onSubmit={onDashboardSave}
|
|
||||||
options={options}
|
|
||||||
onOptionsChange={setOptions}
|
|
||||||
isNew={isNew}
|
|
||||||
isCopy={isCopy}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNew || isCopy) {
|
if (isNew || isCopy) {
|
||||||
return (
|
return (
|
||||||
<SaveDashboardAsForm
|
<SaveDashboardAsForm
|
||||||
|
@ -1,223 +0,0 @@
|
|||||||
import React, { useMemo, useState } from 'react';
|
|
||||||
import { useAsync } from 'react-use';
|
|
||||||
|
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
|
||||||
import { Stack } from '@grafana/experimental';
|
|
||||||
import { locationService } from '@grafana/runtime';
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Checkbox,
|
|
||||||
Field,
|
|
||||||
Form,
|
|
||||||
HorizontalGroup,
|
|
||||||
Input,
|
|
||||||
RadioButtonGroup,
|
|
||||||
Spinner,
|
|
||||||
TextArea,
|
|
||||||
} from '@grafana/ui';
|
|
||||||
import { getGrafanaStorage } from 'app/features/storage/storage';
|
|
||||||
import { ItemOptions, WorkflowID, WriteValueResponse } from 'app/features/storage/types';
|
|
||||||
|
|
||||||
import { SaveProps } from './SaveDashboardForm';
|
|
||||||
|
|
||||||
interface FormDTO {
|
|
||||||
title?: string;
|
|
||||||
message: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props extends SaveProps {
|
|
||||||
isNew?: boolean;
|
|
||||||
isCopy?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SaveToStorageForm(props: Props) {
|
|
||||||
const { dashboard, saveModel, onSubmit, onCancel, onSuccess, onOptionsChange, isNew, isCopy } = props;
|
|
||||||
const hasTimeChanged = useMemo(() => dashboard.hasTimeChanged(), [dashboard]);
|
|
||||||
const hasVariableChanged = useMemo(() => dashboard.hasVariableValuesChanged(), [dashboard]);
|
|
||||||
const [saving, setSaving] = useState(false);
|
|
||||||
const [response, setResponse] = useState<WriteValueResponse>();
|
|
||||||
const [path, setPath] = useState(dashboard.uid);
|
|
||||||
const [workflow, setWorkflow] = useState(WorkflowID.Save);
|
|
||||||
const saveText = useMemo(() => {
|
|
||||||
switch (workflow) {
|
|
||||||
case WorkflowID.PR:
|
|
||||||
return 'Create PR';
|
|
||||||
case WorkflowID.Push:
|
|
||||||
return 'Push';
|
|
||||||
}
|
|
||||||
console.log('???', workflow);
|
|
||||||
return 'Save';
|
|
||||||
}, [workflow]);
|
|
||||||
|
|
||||||
const item = useAsync(async () => {
|
|
||||||
const opts = await getGrafanaStorage().getOptions(dashboard.uid);
|
|
||||||
setWorkflow(opts.workflows[0]?.value ?? WorkflowID.Save);
|
|
||||||
return opts;
|
|
||||||
}, [dashboard.uid]);
|
|
||||||
|
|
||||||
if (item.error) {
|
|
||||||
return <div>Error loading workflows</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.loading || !item.value) {
|
|
||||||
return <Spinner />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{response.url && (
|
|
||||||
<div>
|
|
||||||
<h2>View pull request</h2>
|
|
||||||
<a href={response.url}>{response.url}</a>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<pre>{JSON.stringify(response)}</pre>
|
|
||||||
|
|
||||||
<HorizontalGroup>
|
|
||||||
<Button variant="secondary" onClick={onCancel}>
|
|
||||||
Close
|
|
||||||
</Button>
|
|
||||||
</HorizontalGroup>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let options = props.options;
|
|
||||||
const workflows = item.value?.workflows ?? [];
|
|
||||||
const canSave = saveModel.hasChanges || isNew || isCopy;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
onSubmit={async (data: FormDTO) => {
|
|
||||||
if (!onSubmit) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setSaving(true);
|
|
||||||
|
|
||||||
// Save dashboard without the UID
|
|
||||||
let { uid, ...body } = saveModel.clone;
|
|
||||||
if (isNew || isCopy) {
|
|
||||||
uid = path;
|
|
||||||
if (!uid.endsWith('-dash.json')) {
|
|
||||||
uid += '-dash.json';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const rsp = await getGrafanaStorage().write(uid, {
|
|
||||||
body,
|
|
||||||
kind: 'dashboard',
|
|
||||||
title: data.title,
|
|
||||||
message: data.message,
|
|
||||||
workflow: workflow,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('GOT', rsp);
|
|
||||||
if (rsp.code === 200) {
|
|
||||||
if (options.saveVariables) {
|
|
||||||
dashboard.resetOriginalVariables();
|
|
||||||
}
|
|
||||||
if (options.saveTimerange) {
|
|
||||||
dashboard.resetOriginalTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rsp.pending) {
|
|
||||||
// should close
|
|
||||||
onSuccess();
|
|
||||||
|
|
||||||
// Need to update the URL
|
|
||||||
if (isNew || isCopy) {
|
|
||||||
locationService.push(`/g/${uid}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setSaving(false);
|
|
||||||
}
|
|
||||||
setResponse(rsp);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{({ register, errors }) => (
|
|
||||||
<Stack direction="column" gap={1}>
|
|
||||||
<Stack direction="column" gap={1}>
|
|
||||||
{hasTimeChanged && (
|
|
||||||
<Checkbox
|
|
||||||
checked={!!options.saveTimerange}
|
|
||||||
onChange={() =>
|
|
||||||
onOptionsChange({
|
|
||||||
...options,
|
|
||||||
saveTimerange: !options.saveTimerange,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
label="Save current time range as dashboard default"
|
|
||||||
aria-label={selectors.pages.SaveDashboardModal.saveTimerange}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{hasVariableChanged && (
|
|
||||||
<Checkbox
|
|
||||||
checked={!!options.saveVariables}
|
|
||||||
onChange={() =>
|
|
||||||
onOptionsChange({
|
|
||||||
...options,
|
|
||||||
saveVariables: !options.saveVariables,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
label="Save current variable values as dashboard default"
|
|
||||||
aria-label={selectors.pages.SaveDashboardModal.saveVariables}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
{(isNew || isCopy) && (
|
|
||||||
<Field label="Path">
|
|
||||||
<Input
|
|
||||||
value={path ?? ''}
|
|
||||||
required
|
|
||||||
autoFocus
|
|
||||||
placeholder="Full path (todo, help validate)"
|
|
||||||
onChange={(v) => setPath(v.currentTarget.value)}
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!isJustSave(item.value) && (
|
|
||||||
<Field label="Workflow">
|
|
||||||
<RadioButtonGroup value={workflow} options={workflows} onChange={setWorkflow} />
|
|
||||||
</Field>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{workflow === WorkflowID.PR && (
|
|
||||||
<Field label="PR Title">
|
|
||||||
<Input {...register('title')} required placeholder="Enter a PR title" autoFocus />
|
|
||||||
</Field>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Field label="Message">
|
|
||||||
<TextArea {...register('message')} placeholder="Add a note to describe your changes." rows={5} />
|
|
||||||
</Field>
|
|
||||||
|
|
||||||
<HorizontalGroup>
|
|
||||||
<Button variant="secondary" onClick={onCancel} fill="outline">
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
disabled={!canSave}
|
|
||||||
icon={saving ? 'fa fa-spinner' : undefined}
|
|
||||||
aria-label={selectors.pages.SaveDashboardModal.save}
|
|
||||||
>
|
|
||||||
{saveText}
|
|
||||||
</Button>
|
|
||||||
{!canSave && <div>No changes to save</div>}
|
|
||||||
</HorizontalGroup>
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isJustSave(opts: ItemOptions): boolean {
|
|
||||||
if (opts.workflows.length === 1) {
|
|
||||||
return opts.workflows.find((v) => v.value === WorkflowID.Save) != null;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
@ -8,8 +8,7 @@ import { backendSrv } from 'app/core/services/backend_srv';
|
|||||||
import impressionSrv from 'app/core/services/impression_srv';
|
import impressionSrv from 'app/core/services/impression_srv';
|
||||||
import kbn from 'app/core/utils/kbn';
|
import kbn from 'app/core/utils/kbn';
|
||||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||||
import { getGrafanaStorage } from 'app/features/storage/storage';
|
import { DashboardDataDTO, DashboardDTO, DashboardMeta } from 'app/types';
|
||||||
import { DashboardDataDTO, DashboardDTO, DashboardMeta, DashboardRoutes } from 'app/types';
|
|
||||||
|
|
||||||
import { appEvents } from '../../../core/core';
|
import { appEvents } from '../../../core/core';
|
||||||
|
|
||||||
@ -44,8 +43,6 @@ export class DashboardLoaderSrv {
|
|||||||
promise = backendSrv.get('/api/snapshots/' + slug).catch(() => {
|
promise = backendSrv.get('/api/snapshots/' + slug).catch(() => {
|
||||||
return this._dashboardLoadFailed('Snapshot not found', true);
|
return this._dashboardLoadFailed('Snapshot not found', true);
|
||||||
});
|
});
|
||||||
} else if (type === DashboardRoutes.Path) {
|
|
||||||
promise = getGrafanaStorage().getDashboard(slug!);
|
|
||||||
} else if (type === 'ds') {
|
} else if (type === 'ds') {
|
||||||
promise = this._loadFromDatasource(slug); // explore dashboards as code
|
promise = this._loadFromDatasource(slug); // explore dashboards as code
|
||||||
} else if (type === 'public') {
|
} else if (type === 'public') {
|
||||||
|
@ -3,8 +3,8 @@ import React, { useMemo, useState } from 'react';
|
|||||||
import { useAsync } from 'react-use';
|
import { useAsync } from 'react-use';
|
||||||
|
|
||||||
import { DataFrame, GrafanaTheme2, isDataFrame, ValueLinkConfig } from '@grafana/data';
|
import { DataFrame, GrafanaTheme2, isDataFrame, ValueLinkConfig } from '@grafana/data';
|
||||||
import { config, locationService } from '@grafana/runtime';
|
import { locationService } from '@grafana/runtime';
|
||||||
import { useStyles2, Spinner, TabsBar, Tab, Button, HorizontalGroup, LinkButton, Alert, toIconName } from '@grafana/ui';
|
import { useStyles2, Spinner, TabsBar, Tab, Button, HorizontalGroup, Alert, toIconName } from '@grafana/ui';
|
||||||
import appEvents from 'app/core/app_events';
|
import appEvents from 'app/core/app_events';
|
||||||
import { Page } from 'app/core/components/Page/Page';
|
import { Page } from 'app/core/components/Page/Page';
|
||||||
import { useNavModel } from 'app/core/hooks/useNavModel';
|
import { useNavModel } from 'app/core/hooks/useNavModel';
|
||||||
@ -149,7 +149,6 @@ export default function StoragePage(props: Props) {
|
|||||||
|
|
||||||
const canAddFolder = isFolder && (path.startsWith('resources') || path.startsWith('content'));
|
const canAddFolder = isFolder && (path.startsWith('resources') || path.startsWith('content'));
|
||||||
const canDelete = path.startsWith('resources/') || path.startsWith('content/');
|
const canDelete = path.startsWith('resources/') || path.startsWith('content/');
|
||||||
const canViewDashboard = config.featureToggles.dashboardsFromStorage && path.startsWith('content/');
|
|
||||||
|
|
||||||
const getErrorMessages = () => {
|
const getErrorMessages = () => {
|
||||||
return (
|
return (
|
||||||
@ -172,12 +171,6 @@ export default function StoragePage(props: Props) {
|
|||||||
<HorizontalGroup width="100%" justify="space-between" spacing={'md'} height={25}>
|
<HorizontalGroup width="100%" justify="space-between" spacing={'md'} height={25}>
|
||||||
<Breadcrumb pathName={path} onPathChange={setPath} rootIcon={toIconName(navModel.node.icon ?? '')} />
|
<Breadcrumb pathName={path} onPathChange={setPath} rootIcon={toIconName(navModel.node.icon ?? '')} />
|
||||||
<HorizontalGroup>
|
<HorizontalGroup>
|
||||||
{canViewDashboard && (
|
|
||||||
<LinkButton icon="dashboard" href={`g/${path.substring(path.indexOf('/') + 1)}`}>
|
|
||||||
Dashboard
|
|
||||||
</LinkButton>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{canAddFolder && (
|
{canAddFolder && (
|
||||||
<>
|
<>
|
||||||
<UploadButton path={path} setErrorMessages={setErrorMessages} fileNames={fileNames} setPath={setPath} />
|
<UploadButton path={path} setErrorMessages={setErrorMessages} fileNames={fileNames} setPath={setPath} />
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { DataFrame, dataFrameFromJSON, DataFrameJSON, getDisplayProcessor } from '@grafana/data';
|
import { DataFrame, dataFrameFromJSON, DataFrameJSON, getDisplayProcessor } from '@grafana/data';
|
||||||
import { config, getBackendSrv } from '@grafana/runtime';
|
import { config, getBackendSrv } from '@grafana/runtime';
|
||||||
import { backendSrv } from 'app/core/services/backend_srv';
|
import { backendSrv } from 'app/core/services/backend_srv';
|
||||||
import { DashboardDTO } from 'app/types';
|
|
||||||
|
|
||||||
import { UploadReponse, StorageInfo, ItemOptions, WriteValueRequest, WriteValueResponse } from './types';
|
import { UploadReponse, StorageInfo, ItemOptions, WriteValueRequest, WriteValueResponse } from './types';
|
||||||
|
|
||||||
@ -19,12 +18,6 @@ export interface GrafanaStorage {
|
|||||||
/** Called before save */
|
/** Called before save */
|
||||||
getOptions: (path: string) => Promise<ItemOptions>;
|
getOptions: (path: string) => Promise<ItemOptions>;
|
||||||
|
|
||||||
/**
|
|
||||||
* Temporary shim that will return a DashboardDTO shape for files in storage
|
|
||||||
* Longer term, this will call an "Entity API" that is eventually backed by storage
|
|
||||||
*/
|
|
||||||
getDashboard: (path: string) => Promise<DashboardDTO>;
|
|
||||||
|
|
||||||
/** Saves dashbaords */
|
/** Saves dashbaords */
|
||||||
write: (path: string, options: WriteValueRequest) => Promise<WriteValueResponse>;
|
write: (path: string, options: WriteValueRequest) => Promise<WriteValueResponse>;
|
||||||
}
|
}
|
||||||
@ -121,36 +114,6 @@ class SimpleStorage implements GrafanaStorage {
|
|||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporary shim that can be loaded into the existing dashboard page structure
|
|
||||||
async getDashboard(path: string): Promise<DashboardDTO> {
|
|
||||||
if (!config.featureToggles.dashboardsFromStorage) {
|
|
||||||
return Promise.reject('Dashboards from storage is not enabled');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!path.endsWith('.json')) {
|
|
||||||
path += '.json';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!path.startsWith('content/')) {
|
|
||||||
path = `content/${path}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await backendSrv.get(`/api/storage/read/${path}`);
|
|
||||||
result.uid = path;
|
|
||||||
delete result.id; // Saved with the dev dashboards!
|
|
||||||
|
|
||||||
return {
|
|
||||||
meta: {
|
|
||||||
uid: path,
|
|
||||||
slug: path,
|
|
||||||
canEdit: true,
|
|
||||||
canSave: true,
|
|
||||||
canStar: false, // needs id
|
|
||||||
},
|
|
||||||
dashboard: result,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async write(path: string, options: WriteValueRequest): Promise<WriteValueResponse> {
|
async write(path: string, options: WriteValueRequest): Promise<WriteValueResponse> {
|
||||||
return backendSrv.post<WriteValueResponse>(`/api/storage/write/${path}`, options);
|
return backendSrv.post<WriteValueResponse>(`/api/storage/write/${path}`, options);
|
||||||
}
|
}
|
||||||
|
@ -500,7 +500,6 @@ export function getAppRoutes(): RouteDescriptor[] {
|
|||||||
() => import(/* webpackChunkName: "NotificationsPage"*/ 'app/features/notifications/NotificationsPage')
|
() => import(/* webpackChunkName: "NotificationsPage"*/ 'app/features/notifications/NotificationsPage')
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
...getBrowseStorageRoutes(),
|
|
||||||
...getDynamicDashboardRoutes(),
|
...getDynamicDashboardRoutes(),
|
||||||
...getPluginCatalogRoutes(),
|
...getPluginCatalogRoutes(),
|
||||||
...getSupportBundleRoutes(),
|
...getSupportBundleRoutes(),
|
||||||
@ -519,28 +518,6 @@ export function getAppRoutes(): RouteDescriptor[] {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBrowseStorageRoutes(cfg = config): RouteDescriptor[] {
|
|
||||||
if (!cfg.featureToggles.dashboardsFromStorage) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
path: '/g/:slug*.json', // suffix will eventually include dashboard
|
|
||||||
pageClass: 'page-dashboard',
|
|
||||||
routeName: DashboardRoutes.Path,
|
|
||||||
component: SafeDynamicImport(
|
|
||||||
() => import(/* webpackChunkName: "DashboardPage" */ '../features/dashboard/containers/DashboardPage')
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/g/:slug*',
|
|
||||||
component: SafeDynamicImport(
|
|
||||||
() => import(/* webpackChunkName: "StorageFolderPage" */ '../features/storage/StorageFolderPage')
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getSupportBundleRoutes(cfg = config): RouteDescriptor[] {
|
export function getSupportBundleRoutes(cfg = config): RouteDescriptor[] {
|
||||||
if (!cfg.supportBundlesEnabled) {
|
if (!cfg.supportBundlesEnabled) {
|
||||||
return [];
|
return [];
|
||||||
|
Loading…
Reference in New Issue
Block a user