Skip to content

Commit e66a3d1

Browse files
[9.3] [ResponseOps][MaintenanceWindows] Expose an internal maintenance windows client (#247390) (#247442)
# Backport This will backport the following commits from `main` to `9.3`: - [[ResponseOps][MaintenanceWindows] Expose an internal maintenance windows client (#247390)](#247390) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Janki Salvi","email":"117571355+js-jankisalvi@users.noreply.github.com"},"sourceCommit":{"committedDate":"2025-12-24T10:24:38Z","message":"[ResponseOps][MaintenanceWindows] Expose an internal maintenance windows client (#247390)\n\n## Summary\n\nFixes #246200 by creating MW\nclient using SO's internal repository.\nIt is used in synthetics plugin.\n\n\n### Checklist\n\nCheck the PR satisfies following conditions. \n\n- [x] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios","sha":"25c67f47570502537cf60ec505ae6641b2b20a1f","branchLabelMapping":{"^v9.4.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:ResponseOps","backport:version","Feature:Maintenance Window","v9.3.0","v9.4.0","Team:obs-ux-management"],"title":"[ResponseOps][MaintenanceWindows] Expose an internal maintenance windows client ","number":247390,"url":"https://github.com/elastic/kibana/pull/247390","mergeCommit":{"message":"[ResponseOps][MaintenanceWindows] Expose an internal maintenance windows client (#247390)\n\n## Summary\n\nFixes #246200 by creating MW\nclient using SO's internal repository.\nIt is used in synthetics plugin.\n\n\n### Checklist\n\nCheck the PR satisfies following conditions. \n\n- [x] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios","sha":"25c67f47570502537cf60ec505ae6641b2b20a1f"}},"sourceBranch":"main","suggestedTargetBranches":["9.3"],"targetPullRequestStates":[{"branch":"9.3","label":"v9.3.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v9.4.0","branchLabelMappingKey":"^v9.4.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/247390","number":247390,"mergeCommit":{"message":"[ResponseOps][MaintenanceWindows] Expose an internal maintenance windows client (#247390)\n\n## Summary\n\nFixes #246200 by creating MW\nclient using SO's internal repository.\nIt is used in synthetics plugin.\n\n\n### Checklist\n\nCheck the PR satisfies following conditions. \n\n- [x] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios","sha":"25c67f47570502537cf60ec505ae6641b2b20a1f"}}]}] BACKPORT--> Co-authored-by: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com>
1 parent 84c0668 commit e66a3d1

File tree

10 files changed

+103
-40
lines changed

10 files changed

+103
-40
lines changed

x-pack/platform/plugins/shared/alerting/server/plugin.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -669,11 +669,11 @@ export class AlertingPlugin {
669669
return rulesSettingsClientFactory!.create(request);
670670
};
671671

672-
const getMaintenanceWindowClientInternal = (request: KibanaRequest) => {
672+
const getMaintenanceWindowClient = (request: KibanaRequest) => {
673673
if (!plugins.maintenanceWindows) {
674674
return;
675675
}
676-
return plugins.maintenanceWindows.getMaintenanceWindowClientInternal(request);
676+
return plugins.maintenanceWindows.getMaintenanceWindowClientWithoutAuth(request);
677677
};
678678

679679
taskRunnerFactory.initialize({
@@ -695,7 +695,7 @@ export class AlertingPlugin {
695695
maintenanceWindowsService: new MaintenanceWindowsService({
696696
cacheInterval: this.config.rulesSettings.cacheInterval,
697697
logger,
698-
getMaintenanceWindowClientInternal,
698+
getMaintenanceWindowClient,
699699
}),
700700
maxAlerts: this.config.rules.run.alerts.max,
701701
ruleTypeRegistry: this.ruleTypeRegistry!,

x-pack/platform/plugins/shared/alerting/server/task_runner/maintenance_windows/maintenance_windows_service.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ describe('MaintenanceWindowsService', () => {
6969
test('should load maintenance windows if none in cache', async () => {
7070
maintenanceWindowClient.getActiveMaintenanceWindows.mockResolvedValueOnce(maintenanceWindows);
7171
const maintenanceWindowsService = new MaintenanceWindowsService({
72-
getMaintenanceWindowClientInternal: jest.fn().mockReturnValue(maintenanceWindowClient),
72+
getMaintenanceWindowClient: jest.fn().mockReturnValue(maintenanceWindowClient),
7373
logger,
7474
});
7575
// @ts-ignore - accessing private variable
@@ -103,7 +103,7 @@ describe('MaintenanceWindowsService', () => {
103103
throw new Error('Test error');
104104
});
105105
const maintenanceWindowsService = new MaintenanceWindowsService({
106-
getMaintenanceWindowClientInternal: jest.fn().mockReturnValue(maintenanceWindowClient),
106+
getMaintenanceWindowClient: jest.fn().mockReturnValue(maintenanceWindowClient),
107107
logger,
108108
});
109109
// @ts-ignore - accessing private variable
@@ -139,7 +139,7 @@ describe('MaintenanceWindowsService', () => {
139139
maintenanceWindowClient.getActiveMaintenanceWindows.mockResolvedValueOnce(maintenanceWindows);
140140
maintenanceWindowClient.getActiveMaintenanceWindows.mockResolvedValueOnce(newSpaceMW);
141141
const maintenanceWindowsService = new MaintenanceWindowsService({
142-
getMaintenanceWindowClientInternal: jest.fn().mockReturnValue(maintenanceWindowClient),
142+
getMaintenanceWindowClient: jest.fn().mockReturnValue(maintenanceWindowClient),
143143
logger,
144144
});
145145
// @ts-ignore - accessing private variable
@@ -190,7 +190,7 @@ describe('MaintenanceWindowsService', () => {
190190
test('should use cached windows if cache has not expired', async () => {
191191
maintenanceWindowClient.getActiveMaintenanceWindows.mockResolvedValueOnce(maintenanceWindows);
192192
const maintenanceWindowsService = new MaintenanceWindowsService({
193-
getMaintenanceWindowClientInternal: jest.fn().mockReturnValue(maintenanceWindowClient),
193+
getMaintenanceWindowClient: jest.fn().mockReturnValue(maintenanceWindowClient),
194194
logger,
195195
});
196196

@@ -226,7 +226,7 @@ describe('MaintenanceWindowsService', () => {
226226
maintenanceWindows[0],
227227
]);
228228
const maintenanceWindowsService = new MaintenanceWindowsService({
229-
getMaintenanceWindowClientInternal: jest.fn().mockReturnValue(maintenanceWindowClient),
229+
getMaintenanceWindowClient: jest.fn().mockReturnValue(maintenanceWindowClient),
230230
logger,
231231
});
232232

@@ -263,7 +263,7 @@ describe('MaintenanceWindowsService', () => {
263263
throw new Error('Test error');
264264
});
265265
const maintenanceWindowsService = new MaintenanceWindowsService({
266-
getMaintenanceWindowClientInternal: jest.fn().mockReturnValue(maintenanceWindowClient),
266+
getMaintenanceWindowClient: jest.fn().mockReturnValue(maintenanceWindowClient),
267267
logger,
268268
});
269269

@@ -303,7 +303,7 @@ describe('MaintenanceWindowsService', () => {
303303
];
304304
maintenanceWindowClient.getActiveMaintenanceWindows.mockResolvedValueOnce(mw);
305305
const maintenanceWindowsService = new MaintenanceWindowsService({
306-
getMaintenanceWindowClientInternal: jest.fn().mockReturnValue(maintenanceWindowClient),
306+
getMaintenanceWindowClient: jest.fn().mockReturnValue(maintenanceWindowClient),
307307
logger,
308308
});
309309

@@ -353,7 +353,7 @@ describe('MaintenanceWindowsService', () => {
353353
}));
354354
maintenanceWindowClient.getActiveMaintenanceWindows.mockResolvedValueOnce(mw);
355355
const maintenanceWindowsService = new MaintenanceWindowsService({
356-
getMaintenanceWindowClientInternal: jest.fn().mockReturnValue(maintenanceWindowClient),
356+
getMaintenanceWindowClient: jest.fn().mockReturnValue(maintenanceWindowClient),
357357
logger,
358358
});
359359

@@ -407,7 +407,7 @@ describe('MaintenanceWindowsService', () => {
407407
];
408408
maintenanceWindowClient.getActiveMaintenanceWindows.mockResolvedValueOnce(mw);
409409
const maintenanceWindowsService = new MaintenanceWindowsService({
410-
getMaintenanceWindowClientInternal: jest.fn().mockReturnValue(maintenanceWindowClient),
410+
getMaintenanceWindowClient: jest.fn().mockReturnValue(maintenanceWindowClient),
411411
logger,
412412
});
413413

x-pack/platform/plugins/shared/alerting/server/task_runner/maintenance_windows/maintenance_windows_service.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ export const DEFAULT_CACHE_INTERVAL_MS = 60000; // 1 minute cache
1616

1717
interface MaintenanceWindowServiceOpts {
1818
cacheInterval?: number;
19-
getMaintenanceWindowClientInternal: (
20-
request: KibanaRequest
21-
) => MaintenanceWindowClient | undefined;
19+
getMaintenanceWindowClient: (request: KibanaRequest) => MaintenanceWindowClient | undefined;
2220
logger: Logger;
2321
}
2422

@@ -135,7 +133,7 @@ export class MaintenanceWindowsService {
135133
now: number
136134
): Promise<MaintenanceWindow[]> {
137135
return await withAlertingSpan('alerting:load-maintenance-windows', async () => {
138-
const maintenanceWindowClient = this.options.getMaintenanceWindowClientInternal(request);
136+
const maintenanceWindowClient = this.options.getMaintenanceWindowClient(request);
139137
const activeMaintenanceWindows = maintenanceWindowClient
140138
? await maintenanceWindowClient.getActiveMaintenanceWindows(this.cacheIntervalMs)
141139
: [];

x-pack/platform/plugins/shared/maintenance_windows/server/maintenance_window_client_factory.test.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ test('creates an unauthorized maintenance window client', async () => {
8787

8888
savedObjectsService.getScopedClient.mockReturnValue(savedObjectsClient);
8989

90-
factory.create(request);
90+
factory.createWithoutAuthorization(request);
9191

9292
expect(savedObjectsService.getScopedClient).toHaveBeenCalledWith(request, {
9393
excludedExtensions: [SECURITY_EXTENSION_ID],
@@ -103,6 +103,26 @@ test('creates an unauthorized maintenance window client', async () => {
103103
});
104104
});
105105

106+
test('creates an internal maintenance window client', async () => {
107+
const factory = new MaintenanceWindowClientFactory();
108+
factory.initialize(maintenanceWindowClientFactoryParams);
109+
const request = mockRouter.createKibanaRequest();
110+
const mockRepository = savedObjectsService.createInternalRepository();
111+
savedObjectsService.createInternalRepository.mockReturnValue(mockRepository);
112+
113+
factory.createInternal(request);
114+
115+
expect(savedObjectsService.createInternalRepository).toHaveBeenCalledWith();
116+
117+
const { MaintenanceWindowClient } = jest.requireMock('./client');
118+
119+
expect(MaintenanceWindowClient).toHaveBeenCalledWith({
120+
logger: maintenanceWindowClientFactoryParams.logger,
121+
savedObjectsClient: mockRepository,
122+
getUserName: expect.any(Function),
123+
});
124+
});
125+
106126
test('getUserName() returns null when security is disabled', async () => {
107127
const factory = new MaintenanceWindowClientFactory();
108128
factory.initialize(maintenanceWindowClientFactoryParams);

x-pack/platform/plugins/shared/maintenance_windows/server/maintenance_window_client_factory.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import type {
99
KibanaRequest,
1010
Logger,
11+
SavedObjectsClientContract,
1112
SavedObjectsServiceStart,
1213
SecurityServiceStart,
1314
UiSettingsServiceStart,
@@ -41,19 +42,18 @@ export class MaintenanceWindowClientFactory {
4142
this.uiSettings = options.uiSettings;
4243
}
4344

45+
private getSoClient(request: KibanaRequest, withAuth: boolean): SavedObjectsClientContract {
46+
return this.savedObjectsService.getScopedClient(request, {
47+
includedHiddenTypes: [MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE],
48+
...(withAuth ? {} : { excludedExtensions: [SECURITY_EXTENSION_ID] }),
49+
});
50+
}
51+
4452
private createMaintenanceWindowClient(
4553
request: KibanaRequest,
46-
withAuth: boolean,
47-
excludedExtensions?: ['spaces']
54+
savedObjectsClient: SavedObjectsClientContract
4855
) {
4956
const { securityService } = this;
50-
const savedObjectsClient = this.savedObjectsService.getScopedClient(request, {
51-
includedHiddenTypes: [MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE],
52-
...(withAuth
53-
? {}
54-
: { excludedExtensions: [...(excludedExtensions ?? []), SECURITY_EXTENSION_ID] }),
55-
});
56-
5757
const uiSettingClient = this.uiSettings.asScopedToClient(savedObjectsClient);
5858

5959
return new MaintenanceWindowClient({
@@ -68,10 +68,17 @@ export class MaintenanceWindowClientFactory {
6868
}
6969

7070
public createWithAuthorization(request: KibanaRequest) {
71-
return this.createMaintenanceWindowClient(request, true);
71+
const soClient = this.getSoClient(request, true);
72+
return this.createMaintenanceWindowClient(request, soClient);
73+
}
74+
75+
public createWithoutAuthorization(request: KibanaRequest) {
76+
const soClient = this.getSoClient(request, false);
77+
return this.createMaintenanceWindowClient(request, soClient);
7278
}
7379

74-
public create(request: KibanaRequest, excludedExtension?: ['spaces']) {
75-
return this.createMaintenanceWindowClient(request, false, excludedExtension);
80+
public createInternal(request: KibanaRequest) {
81+
const savedObjectsInternalClient = this.savedObjectsService.createInternalRepository();
82+
return this.createMaintenanceWindowClient(request, savedObjectsInternalClient);
7683
}
7784
}

x-pack/platform/plugins/shared/maintenance_windows/server/mocks.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ const createStartMock = () => {
1717
getMaintenanceWindowClientWithAuth: jest
1818
.fn()
1919
.mockResolvedValue(maintenanceWindowClientMock.create()),
20+
getMaintenanceWindowClientWithoutAuth: jest
21+
.fn()
22+
.mockResolvedValue(maintenanceWindowClientMock.create()),
2023
});
2124

2225
return mock;

x-pack/platform/plugins/shared/maintenance_windows/server/plugin.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,40 @@ describe('Maintenance Windows Plugin', () => {
9999
expect(client).toBeDefined();
100100
});
101101

102+
test(`exposes getMaintenanceWindowClientWithoutAuth()`, async () => {
103+
const context = coreMock.createPluginInitializerContext();
104+
const plugin = new MaintenanceWindowsPlugin(context);
105+
106+
plugin.setup(coreMock.createSetup(), {
107+
licensing: licensingMock.createSetup(),
108+
taskManager: taskManagerMock.createSetup(),
109+
features: featuresPluginMock.createSetup(),
110+
});
111+
112+
const startContract = plugin.start(coreMock.createStart(), {
113+
taskManager: taskManagerMock.createStart(),
114+
});
115+
116+
const fakeRequest = {
117+
headers: {},
118+
getBasePath: () => '',
119+
path: '/',
120+
route: { settings: {} },
121+
url: {
122+
href: '/',
123+
},
124+
raw: {
125+
req: {
126+
url: '/',
127+
},
128+
},
129+
getSavedObjectsClient: jest.fn(),
130+
} as unknown as KibanaRequest;
131+
132+
const client = startContract.getMaintenanceWindowClientWithoutAuth(fakeRequest);
133+
expect(client).toBeDefined();
134+
});
135+
102136
test(`exposes getMaintenanceWindowClientInternal()`, async () => {
103137
const context =
104138
coreMock.createPluginInitializerContext<MaintenanceWindowsConfig>(maintenanceWindowsConfig);

x-pack/platform/plugins/shared/maintenance_windows/server/plugin.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,17 +111,23 @@ export class MaintenanceWindowsPlugin
111111
return maintenanceWindowClientFactory.createWithAuthorization(request);
112112
};
113113

114+
const getMaintenanceWindowClientWithoutAuth = (
115+
request: KibanaRequest
116+
): MaintenanceWindowClientApi => {
117+
return maintenanceWindowClientFactory.createWithoutAuthorization(request);
118+
};
119+
114120
const getMaintenanceWindowClientInternal = (
115-
request: KibanaRequest,
116-
excludedExtension?: ['spaces']
121+
request: KibanaRequest
117122
): MaintenanceWindowClientApi => {
118-
return maintenanceWindowClientFactory.create(request, excludedExtension);
123+
return maintenanceWindowClientFactory.createInternal(request);
119124
};
120125

121126
scheduleMaintenanceWindowEventsGenerator(this.logger, plugins.taskManager).catch(() => {});
122127

123128
return {
124129
getMaintenanceWindowClientWithAuth,
130+
getMaintenanceWindowClientWithoutAuth,
125131
getMaintenanceWindowClientInternal,
126132
};
127133
}

x-pack/platform/plugins/shared/maintenance_windows/server/types.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,7 @@ export interface MaintenanceWindowsServerStartDependencies {
3838
}
3939

4040
export interface MaintenanceWindowsServerStart {
41-
getMaintenanceWindowClientInternal(
42-
request: KibanaRequest,
43-
excludedExtension?: string[]
44-
): MaintenanceWindowClientApi;
41+
getMaintenanceWindowClientInternal(request: KibanaRequest): MaintenanceWindowClientApi;
4542
getMaintenanceWindowClientWithAuth(request: KibanaRequest): MaintenanceWindowClientApi;
43+
getMaintenanceWindowClientWithoutAuth(request: KibanaRequest): MaintenanceWindowClientApi;
4644
}

x-pack/solutions/observability/plugins/synthetics/server/plugin.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import type {
1313
SavedObjectsClientContract,
1414
KibanaRequest,
1515
} from '@kbn/core/server';
16-
import { SPACES_EXTENSION_ID } from '@kbn/core/server';
1716
import { SavedObjectsClient } from '@kbn/core/server';
1817
import { mappingFromFieldMap } from '@kbn/alerting-plugin/common';
1918
import { Dataset } from '@kbn/rule-registry-plugin/server';
@@ -130,9 +129,7 @@ export class Plugin implements PluginType {
130129
return;
131130
}
132131

133-
return pluginsStart.maintenanceWindows?.getMaintenanceWindowClientInternal(request, [
134-
SPACES_EXTENSION_ID,
135-
]);
132+
return pluginsStart.maintenanceWindows?.getMaintenanceWindowClientInternal(request);
136133
};
137134

138135
if (this.server) {

0 commit comments

Comments
 (0)