|
4 | 4 | * you may not use this file except in compliance with the Elastic License. |
5 | 5 | */ |
6 | 6 |
|
7 | | -import { IScopedClusterClient } from 'kibana/server'; |
| 7 | +import Boom from '@hapi/boom'; |
| 8 | +import { IScopedClusterClient, KibanaRequest } from 'kibana/server'; |
8 | 9 | import type { JobSavedObjectService } from './service'; |
9 | | -import { JobType } from '../../common/types/saved_objects'; |
| 10 | +import { JobType, DeleteJobCheckResponse } from '../../common/types/saved_objects'; |
10 | 11 |
|
11 | 12 | import { Job } from '../../common/types/anomaly_detection_jobs'; |
12 | 13 | import { Datafeed } from '../../common/types/anomaly_detection_jobs'; |
13 | 14 | import { DataFrameAnalyticsConfig } from '../../common/types/data_frame_analytics'; |
| 15 | +import { ResolveMlCapabilities } from '../../common/types/capabilities'; |
14 | 16 |
|
15 | 17 | interface JobSavedObjectStatus { |
16 | 18 | jobId: string; |
@@ -154,5 +156,105 @@ export function checksFactory( |
154 | 156 | }; |
155 | 157 | } |
156 | 158 |
|
157 | | - return { checkStatus }; |
| 159 | + async function canDeleteJobs( |
| 160 | + request: KibanaRequest, |
| 161 | + jobType: JobType, |
| 162 | + jobIds: string[], |
| 163 | + spacesEnabled: boolean, |
| 164 | + resolveMlCapabilities: ResolveMlCapabilities |
| 165 | + ) { |
| 166 | + if (jobType !== 'anomaly-detector' && jobType !== 'data-frame-analytics') { |
| 167 | + throw Boom.badRequest('Job type must be "anomaly-detector" or "data-frame-analytics"'); |
| 168 | + } |
| 169 | + |
| 170 | + const mlCapabilities = await resolveMlCapabilities(request); |
| 171 | + if (mlCapabilities === null) { |
| 172 | + throw Boom.internal('mlCapabilities is not defined'); |
| 173 | + } |
| 174 | + |
| 175 | + if ( |
| 176 | + (jobType === 'anomaly-detector' && mlCapabilities.canDeleteJob === false) || |
| 177 | + (jobType === 'data-frame-analytics' && mlCapabilities.canDeleteDataFrameAnalytics === false) |
| 178 | + ) { |
| 179 | + // user does not have access to delete jobs. |
| 180 | + return jobIds.reduce((results, jobId) => { |
| 181 | + results[jobId] = { |
| 182 | + canDelete: false, |
| 183 | + canUntag: false, |
| 184 | + }; |
| 185 | + return results; |
| 186 | + }, {} as DeleteJobCheckResponse); |
| 187 | + } |
| 188 | + |
| 189 | + if (spacesEnabled === false) { |
| 190 | + // spaces are disabled, delete only no untagging |
| 191 | + return jobIds.reduce((results, jobId) => { |
| 192 | + results[jobId] = { |
| 193 | + canDelete: true, |
| 194 | + canUntag: false, |
| 195 | + }; |
| 196 | + return results; |
| 197 | + }, {} as DeleteJobCheckResponse); |
| 198 | + } |
| 199 | + const canCreateGlobalJobs = await jobSavedObjectService.canCreateGlobalJobs(request); |
| 200 | + |
| 201 | + const jobObjects = await Promise.all( |
| 202 | + jobIds.map((id) => jobSavedObjectService.getJobObject(jobType, id)) |
| 203 | + ); |
| 204 | + |
| 205 | + return jobIds.reduce((results, jobId) => { |
| 206 | + const jobObject = jobObjects.find((j) => j?.attributes.job_id === jobId); |
| 207 | + if (jobObject === undefined || jobObject.namespaces === undefined) { |
| 208 | + // job saved object not found |
| 209 | + results[jobId] = { |
| 210 | + canDelete: false, |
| 211 | + canUntag: false, |
| 212 | + }; |
| 213 | + return results; |
| 214 | + } |
| 215 | + |
| 216 | + const { namespaces } = jobObject; |
| 217 | + const isGlobalJob = namespaces.includes('*'); |
| 218 | + |
| 219 | + // job is in * space, user can see all spaces - delete and no option to untag |
| 220 | + if (canCreateGlobalJobs && isGlobalJob) { |
| 221 | + results[jobId] = { |
| 222 | + canDelete: true, |
| 223 | + canUntag: false, |
| 224 | + }; |
| 225 | + return results; |
| 226 | + } |
| 227 | + |
| 228 | + // job is in * space, user cannot see all spaces - no untagging, no deleting |
| 229 | + if (isGlobalJob) { |
| 230 | + results[jobId] = { |
| 231 | + canDelete: false, |
| 232 | + canUntag: false, |
| 233 | + }; |
| 234 | + return results; |
| 235 | + } |
| 236 | + |
| 237 | + // jobs with are in individual spaces can only be untagged |
| 238 | + // from current space if the job is in more than 1 space |
| 239 | + const canUntag = namespaces.length > 1; |
| 240 | + |
| 241 | + // job is in individual spaces, user cannot see all of them - untag only, no delete |
| 242 | + if (namespaces.includes('?')) { |
| 243 | + results[jobId] = { |
| 244 | + canDelete: false, |
| 245 | + canUntag, |
| 246 | + }; |
| 247 | + return results; |
| 248 | + } |
| 249 | + |
| 250 | + // job is individual spaces, user can see all of them - delete and option to untag |
| 251 | + results[jobId] = { |
| 252 | + canDelete: true, |
| 253 | + canUntag, |
| 254 | + }; |
| 255 | + return results; |
| 256 | + }, {} as DeleteJobCheckResponse); |
| 257 | + } |
| 258 | + |
| 259 | + return { checkStatus, canDeleteJobs }; |
158 | 260 | } |
0 commit comments