Skip to content

Commit 37b7f70

Browse files
Merge branch '7.x' into backport/7.x/pr-92644
2 parents 66730e3 + 894fada commit 37b7f70

263 files changed

Lines changed: 3355 additions & 1563 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/developer/plugin-list.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ which also contains the timelion APIs and backend, look at the vis_type_timelion
220220
|This plugins contains helpers to redirect legacy URLs. It can be used to forward old URLs to their new counterparts.
221221
222222
223-
|{kib-repo}blob/{branch}/src/plugins/usage_collection/README.md[usageCollection]
223+
|{kib-repo}blob/{branch}/src/plugins/usage_collection/README.mdx[usageCollection]
224224
|The Usage Collection Service defines a set of APIs for other plugins to report the usage of their features. At the same time, it provides necessary the APIs for other services (i.e.: telemetry, monitoring, ...) to consume that usage data.
225225
226226

docs/user/alerting/alert-types.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ For domain-specific alerts, refer to the documentation for that app.
2828
* {observability-guide}/create-alerts.html[Observability alerts]
2929
* {security-guide}/prebuilt-rules.html[Security alerts]
3030
* <<geo-alerting, Maps alerts>>
31-
* <<xpack-ml, ML alerts>>
31+
* {ml-docs}/ml-configuring-alerts.html[{ml-cap} alerts]
3232

3333
[NOTE]
3434
==============================================

docs/user/alerting/alerting-getting-started.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
--
77

8-
Alerting allows you to detect complex conditions within different {kib} apps and trigger actions when those conditions are met. Alerting is integrated with {observability-guide}/create-alerts.html[*Observability*], {security-guide}/prebuilt-rules.html[*Security*], <<geo-alerting,*Maps*>> and <<xpack-ml,*ML*>>, can be centrally managed from the <<management,*Management*>> UI, and provides a set of built-in <<action-types, actions>> and <<alert-types, alerts>> (known as stack alerts) for you to use.
8+
Alerting allows you to detect complex conditions within different {kib} apps and trigger actions when those conditions are met. Alerting is integrated with {observability-guide}/create-alerts.html[*Observability*], {security-guide}/prebuilt-rules.html[*Security*], <<geo-alerting,*Maps*>> and {ml-docs}/ml-configuring-alerts.html[*{ml-app}*], can be centrally managed from the <<management,*Management*>> UI, and provides a set of built-in <<action-types, actions>> and <<alert-types, alerts>> (known as stack alerts) for you to use.
99

1010
image::images/alerting-overview.png[Alerts and actions UI]
1111

docs/user/alerting/defining-alerts.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
[[defining-alerts]]
33
== Defining alerts
44

5-
{kib} alerts can be created in a variety of apps including <<xpack-apm,*APM*>>, <<metrics-app,*Metrics*>>, <<xpack-siem,*Security*>>, <<uptime-app,*Uptime*>> and from <<management,*Management*>> UI. While alerting details may differ from app to app, they share a common interface for defining and configuring alerts that this section describes in more detail.
5+
{kib} alerts can be created in a variety of apps including <<xpack-apm,*APM*>>, <<xpack-ml,*{ml-app}*>>, <<metrics-app,*Metrics*>>, <<xpack-siem,*Security*>>, <<uptime-app,*Uptime*>> and from <<management,*Management*>> UI. While alerting details may differ from app to app, they share a common interface for defining and configuring alerts that this section describes in more detail.
66

77
[float]
88
=== Alert flyout

src/core/CONVENTIONS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ export class Plugin {
285285

286286
#### Usage Collection
287287

288-
For creating and registering a Usage Collector. Collectors should be defined in a separate directory `server/collectors/`. You can read more about usage collectors on `src/plugins/usage_collection/README.md`.
288+
For creating and registering a Usage Collector. Collectors should be defined in a separate directory `server/collectors/`. You can read more about usage collectors on `src/plugins/usage_collection/README.mdx`.
289289

290290
```ts
291291
// server/collectors/register.ts

src/core/server/saved_objects/service/lib/repository.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1640,7 +1640,7 @@ export class SavedObjectsRepository {
16401640
*
16411641
* When using incrementCounter for collecting usage data, you need to ensure
16421642
* that usage collection happens on a best-effort basis and doesn't
1643-
* negatively affect your plugin or users. See https://github.com/elastic/kibana/blob/master/src/plugins/usage_collection/README.md#tracking-interactions-with-incrementcounter)
1643+
* negatively affect your plugin or users. See https://github.com/elastic/kibana/blob/master/src/plugins/usage_collection/README.mdx#tracking-interactions-with-incrementcounter)
16441644
*
16451645
* @example
16461646
* ```ts

src/plugins/dashboard/public/application/dashboard_app.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,26 @@ export function DashboardApp({
6363
const [indexPatterns, setIndexPatterns] = useState<IndexPattern[]>([]);
6464

6565
const savedDashboard = useSavedDashboard(savedDashboardId, history);
66+
67+
const getIncomingEmbeddable = useCallback(
68+
(removeAfterFetch?: boolean) => {
69+
return embeddable
70+
.getStateTransfer()
71+
.getIncomingEmbeddablePackage(DashboardConstants.DASHBOARDS_ID, removeAfterFetch);
72+
},
73+
[embeddable]
74+
);
75+
6676
const { dashboardStateManager, viewMode, setViewMode } = useDashboardStateManager(
6777
savedDashboard,
68-
history
78+
history,
79+
getIncomingEmbeddable
6980
);
7081
const [unsavedChanges, setUnsavedChanges] = useState(false);
7182
const dashboardContainer = useDashboardContainer({
7283
timeFilter: data.query.timefilter.timefilter,
7384
dashboardStateManager,
85+
getIncomingEmbeddable,
7486
setUnsavedChanges,
7587
history,
7688
});

src/plugins/dashboard/public/application/dashboard_state.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ describe('DashboardState', function () {
4343
savedDashboard,
4444
hideWriteControls: false,
4545
allowByValueEmbeddables: false,
46+
hasPendingEmbeddable: () => false,
4647
kibanaVersion: '7.0.0',
4748
kbnUrlStateStorage: createKbnUrlStateStorage(),
4849
history: createBrowserHistory(),
@@ -199,4 +200,73 @@ describe('DashboardState', function () {
199200
expect(dashboardState.getIsDirty()).toBeFalsy();
200201
});
201202
});
203+
204+
describe('initial view mode', () => {
205+
test('initial view mode set to view when hideWriteControls is true', () => {
206+
const initialViewModeDashboardState = new DashboardStateManager({
207+
savedDashboard,
208+
hideWriteControls: true,
209+
allowByValueEmbeddables: false,
210+
hasPendingEmbeddable: () => false,
211+
kibanaVersion: '7.0.0',
212+
kbnUrlStateStorage: createKbnUrlStateStorage(),
213+
history: createBrowserHistory(),
214+
toasts: coreMock.createStart().notifications.toasts,
215+
hasTaggingCapabilities: mockHasTaggingCapabilities,
216+
});
217+
expect(initialViewModeDashboardState.getViewMode()).toBe(ViewMode.VIEW);
218+
});
219+
220+
test('initial view mode set to edit if edit mode specified in URL', () => {
221+
const kbnUrlStateStorage = createKbnUrlStateStorage();
222+
kbnUrlStateStorage.set('_a', { viewMode: ViewMode.EDIT });
223+
224+
const initialViewModeDashboardState = new DashboardStateManager({
225+
savedDashboard,
226+
kbnUrlStateStorage,
227+
kibanaVersion: '7.0.0',
228+
hideWriteControls: false,
229+
allowByValueEmbeddables: false,
230+
history: createBrowserHistory(),
231+
hasPendingEmbeddable: () => false,
232+
toasts: coreMock.createStart().notifications.toasts,
233+
hasTaggingCapabilities: mockHasTaggingCapabilities,
234+
});
235+
expect(initialViewModeDashboardState.getViewMode()).toBe(ViewMode.EDIT);
236+
});
237+
238+
test('initial view mode set to edit if the dashboard is new', () => {
239+
const newDashboard = getSavedDashboardMock();
240+
newDashboard.id = undefined;
241+
const initialViewModeDashboardState = new DashboardStateManager({
242+
savedDashboard: newDashboard,
243+
kibanaVersion: '7.0.0',
244+
hideWriteControls: false,
245+
allowByValueEmbeddables: false,
246+
history: createBrowserHistory(),
247+
hasPendingEmbeddable: () => false,
248+
kbnUrlStateStorage: createKbnUrlStateStorage(),
249+
toasts: coreMock.createStart().notifications.toasts,
250+
hasTaggingCapabilities: mockHasTaggingCapabilities,
251+
});
252+
expect(initialViewModeDashboardState.getViewMode()).toBe(ViewMode.EDIT);
253+
});
254+
255+
test('initial view mode set to edit if there is a pending embeddable', () => {
256+
const newDashboard = getSavedDashboardMock();
257+
newDashboard.id = undefined;
258+
const initialViewModeDashboardState = new DashboardStateManager({
259+
savedDashboard: newDashboard,
260+
kibanaVersion: '7.0.0',
261+
hideWriteControls: false,
262+
allowByValueEmbeddables: false,
263+
history: createBrowserHistory(),
264+
hasPendingEmbeddable: () => true,
265+
kbnUrlStateStorage: createKbnUrlStateStorage(),
266+
toasts: coreMock.createStart().notifications.toasts,
267+
hasTaggingCapabilities: mockHasTaggingCapabilities,
268+
});
269+
expect(initialViewModeDashboardState.getViewMode()).toBe(ViewMode.EDIT);
270+
});
271+
});
202272
});

src/plugins/dashboard/public/application/dashboard_state_manager.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ export class DashboardStateManager {
8888

8989
private readonly usageCollection: UsageCollectionSetup | undefined;
9090
public readonly hasTaggingCapabilities: SavedObjectTagDecoratorTypeGuard;
91+
private hasPendingEmbeddable: () => boolean;
9192

9293
/**
9394
*
@@ -104,13 +105,15 @@ export class DashboardStateManager {
104105
usageCollection,
105106
hideWriteControls,
106107
kbnUrlStateStorage,
108+
hasPendingEmbeddable,
107109
dashboardPanelStorage,
108110
hasTaggingCapabilities,
109111
allowByValueEmbeddables,
110112
}: {
111113
history: History;
112114
kibanaVersion: string;
113115
hideWriteControls: boolean;
116+
hasPendingEmbeddable: () => boolean;
114117
allowByValueEmbeddables: boolean;
115118
savedDashboard: DashboardSavedObject;
116119
toasts: NotificationsStart['toasts'];
@@ -126,15 +129,17 @@ export class DashboardStateManager {
126129
this.usageCollection = usageCollection;
127130
this.hasTaggingCapabilities = hasTaggingCapabilities;
128131
this.allowByValueEmbeddables = allowByValueEmbeddables;
132+
this.hasPendingEmbeddable = hasPendingEmbeddable;
133+
this.dashboardPanelStorage = dashboardPanelStorage;
134+
this.kbnUrlStateStorage = kbnUrlStateStorage;
129135

130136
// get state defaults from saved dashboard, make sure it is migrated
137+
const viewMode = this.getInitialViewMode();
131138
this.stateDefaults = migrateAppState(
132-
getAppStateDefaults(this.savedDashboard, this.hideWriteControls, this.hasTaggingCapabilities),
139+
getAppStateDefaults(viewMode, this.savedDashboard, this.hasTaggingCapabilities),
133140
kibanaVersion,
134141
usageCollection
135142
);
136-
this.dashboardPanelStorage = dashboardPanelStorage;
137-
this.kbnUrlStateStorage = kbnUrlStateStorage;
138143

139144
// setup initial state by merging defaults with state from url & panels storage
140145
// also run migration, as state in url could be of older version
@@ -357,8 +362,9 @@ export class DashboardStateManager {
357362
// The right way to fix this might be to ensure the defaults object stored on state is a deep
358363
// clone, but given how much code uses the state object, I determined that to be too risky of a change for
359364
// now. TODO: revisit this!
365+
const currentViewMode = this.stateContainer.get().viewMode;
360366
this.stateDefaults = migrateAppState(
361-
getAppStateDefaults(this.savedDashboard, this.hideWriteControls, this.hasTaggingCapabilities),
367+
getAppStateDefaults(currentViewMode, this.savedDashboard, this.hasTaggingCapabilities),
362368
this.kibanaVersion,
363369
this.usageCollection
364370
);
@@ -369,8 +375,7 @@ export class DashboardStateManager {
369375
this.stateDefaults.filters = [...this.getLastSavedFilterBars()];
370376
this.isDirty = false;
371377

372-
const currentViewMode = this.stateContainer.get().viewMode;
373-
this.stateContainer.set({ ...this.stateDefaults, viewMode: currentViewMode });
378+
this.stateContainer.set(this.stateDefaults);
374379
}
375380

376381
/**
@@ -534,9 +539,7 @@ export class DashboardStateManager {
534539
return this.appState.viewMode;
535540
}
536541
// get viewMode should work properly even before the state container is created
537-
return this.savedDashboard.id
538-
? this.kbnUrlStateStorage.get<DashboardAppState>(STATE_STORAGE_KEY)?.viewMode ?? ViewMode.VIEW
539-
: ViewMode.EDIT;
542+
return this.getInitialViewMode();
540543
}
541544

542545
public getIsViewMode() {
@@ -668,6 +671,7 @@ export class DashboardStateManager {
668671

669672
public switchViewMode(newMode: ViewMode) {
670673
this.stateContainer.transitions.set('viewMode', newMode);
674+
this.restorePanels();
671675
}
672676

673677
/**
@@ -692,6 +696,7 @@ export class DashboardStateManager {
692696
...this.stateDefaults,
693697
...unsavedState,
694698
...this.kbnUrlStateStorage.get<DashboardAppState>(STATE_STORAGE_KEY),
699+
viewMode: this.getViewMode(),
695700
},
696701
this.kibanaVersion,
697702
this.usageCollection
@@ -739,6 +744,18 @@ export class DashboardStateManager {
739744
return stateWithoutPanels;
740745
}
741746

747+
private getInitialViewMode() {
748+
if (this.hideWriteControls) {
749+
return ViewMode.VIEW;
750+
}
751+
const viewModeFromUrl = this.kbnUrlStateStorage.get<DashboardAppState>(STATE_STORAGE_KEY)
752+
?.viewMode;
753+
if (viewModeFromUrl) {
754+
return viewModeFromUrl;
755+
}
756+
return !this.savedDashboard.id || this.hasPendingEmbeddable() ? ViewMode.EDIT : ViewMode.VIEW;
757+
}
758+
742759
private checkIsDirty() {
743760
// Filters need to be compared manually because they sometimes have a $$hashkey stored on the object.
744761
// Query needs to be compared manually because saved legacy queries get migrated in app state automatically

src/plugins/dashboard/public/application/hooks/use_dashboard_container.test.tsx

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const createDashboardState = () =>
3737
hideWriteControls: false,
3838
allowByValueEmbeddables: false,
3939
history: createBrowserHistory(),
40+
hasPendingEmbeddable: () => false,
4041
kbnUrlStateStorage: createKbnUrlStateStorage(),
4142
hasTaggingCapabilities: mockHasTaggingCapabilities,
4243
toasts: coreMock.createStart().notifications.toasts,
@@ -53,6 +54,8 @@ const defaultCapabilities: DashboardCapabilities = {
5354
storeSearchSession: true,
5455
};
5556

57+
const getIncomingEmbeddable = () => undefined;
58+
5659
const services = {
5760
dashboardCapabilities: defaultCapabilities,
5861
data: dataPluginMock.createStartContract(),
@@ -87,7 +90,12 @@ test('container is destroyed on unmount', async () => {
8790

8891
const dashboardStateManager = createDashboardState();
8992
const { result, unmount, waitForNextUpdate } = renderHook(
90-
() => useDashboardContainer({ dashboardStateManager, history }),
93+
() =>
94+
useDashboardContainer({
95+
getIncomingEmbeddable,
96+
dashboardStateManager,
97+
history,
98+
}),
9199
{
92100
wrapper: ({ children }) => (
93101
<KibanaContextProvider services={services}>{children}</KibanaContextProvider>
@@ -115,12 +123,20 @@ test('old container is destroyed on new dashboardStateManager', async () => {
115123
const { result, waitForNextUpdate, rerender } = renderHook<
116124
DashboardStateManager,
117125
DashboardContainer | null
118-
>((dashboardStateManager) => useDashboardContainer({ dashboardStateManager, history }), {
119-
wrapper: ({ children }) => (
120-
<KibanaContextProvider services={services}>{children}</KibanaContextProvider>
121-
),
122-
initialProps: createDashboardState(),
123-
});
126+
>(
127+
(dashboardStateManager) =>
128+
useDashboardContainer({
129+
getIncomingEmbeddable,
130+
dashboardStateManager,
131+
history,
132+
}),
133+
{
134+
wrapper: ({ children }) => (
135+
<KibanaContextProvider services={services}>{children}</KibanaContextProvider>
136+
),
137+
initialProps: createDashboardState(),
138+
}
139+
);
124140

125141
expect(result.current).toBeNull(); // null on initial render
126142

@@ -150,12 +166,20 @@ test('destroyed if rerendered before resolved', async () => {
150166
const { result, waitForNextUpdate, rerender } = renderHook<
151167
DashboardStateManager,
152168
DashboardContainer | null
153-
>((dashboardStateManager) => useDashboardContainer({ dashboardStateManager, history }), {
154-
wrapper: ({ children }) => (
155-
<KibanaContextProvider services={services}>{children}</KibanaContextProvider>
156-
),
157-
initialProps: createDashboardState(),
158-
});
169+
>(
170+
(dashboardStateManager) =>
171+
useDashboardContainer({
172+
getIncomingEmbeddable,
173+
dashboardStateManager,
174+
history,
175+
}),
176+
{
177+
wrapper: ({ children }) => (
178+
<KibanaContextProvider services={services}>{children}</KibanaContextProvider>
179+
),
180+
initialProps: createDashboardState(),
181+
}
182+
);
159183

160184
expect(result.current).toBeNull(); // null on initial render
161185

0 commit comments

Comments
 (0)