mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[ML] Fixing endpoint schema for can_delete_job endpoint (#86436)
* [ML] Fixing endpoint schema for can_delete_job endpoint * changing get to post in docs * renaming canUntag * fixing typo Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
c05533ebbd
commit
6d72042ca4
7 changed files with 45 additions and 30 deletions
|
@ -21,7 +21,7 @@ export interface SyncSavedObjectResponse {
|
|||
export interface CanDeleteJobResponse {
|
||||
[jobId: string]: {
|
||||
canDelete: boolean;
|
||||
canUntag: boolean;
|
||||
canRemoveFromSpace: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -41,5 +41,5 @@ export interface DeleteJobCheckResponse {
|
|||
|
||||
export interface DeleteJobPermission {
|
||||
canDelete: boolean;
|
||||
canUntag: boolean;
|
||||
canRemoveFromSpace: boolean;
|
||||
}
|
||||
|
|
|
@ -36,31 +36,31 @@ interface ModalContentReturnType {
|
|||
|
||||
interface JobCheckRespSummary {
|
||||
canDelete: boolean;
|
||||
canUntag: boolean;
|
||||
canRemoveFromSpace: boolean;
|
||||
canTakeAnyAction: boolean;
|
||||
}
|
||||
|
||||
function getRespSummary(resp: CanDeleteJobResponse): JobCheckRespSummary {
|
||||
const jobsChecked = Object.keys(resp);
|
||||
// Default to first job's permissions
|
||||
const { canDelete, canUntag } = resp[jobsChecked[0]];
|
||||
const { canDelete, canRemoveFromSpace } = resp[jobsChecked[0]];
|
||||
let canTakeAnyAction = true;
|
||||
|
||||
if (jobsChecked.length > 1) {
|
||||
// Check all jobs and make sure they have the same permissions - otherwise no action can be taken
|
||||
canTakeAnyAction = jobsChecked.every(
|
||||
(id) => resp[id].canDelete === canDelete && resp[id].canUntag === canUntag
|
||||
(id) => resp[id].canDelete === canDelete && resp[id].canRemoveFromSpace === canRemoveFromSpace
|
||||
);
|
||||
}
|
||||
|
||||
return { canDelete, canUntag, canTakeAnyAction };
|
||||
return { canDelete, canRemoveFromSpace, canTakeAnyAction };
|
||||
}
|
||||
|
||||
function getModalContent(
|
||||
jobIds: string[],
|
||||
respSummary: JobCheckRespSummary
|
||||
): ModalContentReturnType {
|
||||
const { canDelete, canUntag, canTakeAnyAction } = respSummary;
|
||||
const { canDelete, canRemoveFromSpace, canTakeAnyAction } = respSummary;
|
||||
|
||||
if (canTakeAnyAction === false) {
|
||||
return {
|
||||
|
@ -116,7 +116,7 @@ function getModalContent(
|
|||
</EuiText>
|
||||
),
|
||||
};
|
||||
} else if (canUntag) {
|
||||
} else if (canRemoveFromSpace) {
|
||||
return {
|
||||
buttonText: (
|
||||
<FormattedMessage
|
||||
|
@ -173,8 +173,8 @@ export const DeleteJobCheckModal: FC<Props> = ({
|
|||
// Do the spaces check and set the content for the modal and buttons depending on results
|
||||
canDeleteJob(jobType, jobIds).then((resp) => {
|
||||
const respSummary = getRespSummary(resp);
|
||||
const { canDelete, canUntag, canTakeAnyAction } = respSummary;
|
||||
if (canTakeAnyAction && canDelete && !canUntag) {
|
||||
const { canDelete, canRemoveFromSpace, canTakeAnyAction } = respSummary;
|
||||
if (canTakeAnyAction && canDelete && !canRemoveFromSpace) {
|
||||
// Go straight to delete flow if that's the only action available
|
||||
canDeleteCallback();
|
||||
return;
|
||||
|
@ -260,7 +260,7 @@ export const DeleteJobCheckModal: FC<Props> = ({
|
|||
<EuiFlexItem grow={false}>
|
||||
{!hasUntagged &&
|
||||
jobCheckRespSummary?.canTakeAnyAction &&
|
||||
jobCheckRespSummary?.canUntag &&
|
||||
jobCheckRespSummary?.canRemoveFromSpace &&
|
||||
jobCheckRespSummary?.canDelete && (
|
||||
<EuiButtonEmpty
|
||||
isLoading={isUntagging}
|
||||
|
@ -277,7 +277,7 @@ export const DeleteJobCheckModal: FC<Props> = ({
|
|||
size="s"
|
||||
onClick={
|
||||
jobCheckRespSummary?.canTakeAnyAction &&
|
||||
jobCheckRespSummary?.canUntag &&
|
||||
jobCheckRespSummary?.canRemoveFromSpace &&
|
||||
!jobCheckRespSummary?.canDelete
|
||||
? onUntagClick
|
||||
: onClick
|
||||
|
|
|
@ -151,7 +151,7 @@
|
|||
"RemoveJobsFromSpaces",
|
||||
"RemoveJobsFromCurrentSpace",
|
||||
"JobsSpaces",
|
||||
"DeleteJobCheck",
|
||||
"CanDeleteJob",
|
||||
|
||||
"TrainedModels",
|
||||
"GetTrainedModel",
|
||||
|
|
|
@ -12,8 +12,8 @@ import {
|
|||
jobsAndCurrentSpace,
|
||||
syncJobObjects,
|
||||
jobTypeSchema,
|
||||
canDeleteJobSchema,
|
||||
} from './schemas/saved_objects';
|
||||
import { jobIdsSchema } from './schemas/job_service_schema';
|
||||
import { spacesUtilsProvider } from '../lib/spaces_utils';
|
||||
import { JobType } from '../../common/types/saved_objects';
|
||||
|
||||
|
@ -284,13 +284,23 @@ export function savedObjectsRoutes(
|
|||
/**
|
||||
* @apiGroup JobSavedObjects
|
||||
*
|
||||
* @api {get} /api/ml/saved_objects/delete_job_check Check whether user can delete a job
|
||||
* @apiName DeleteJobCheck
|
||||
* @api {post} /api/ml/saved_objects/can_delete_job Check whether user can delete a job
|
||||
* @apiName CanDeleteJob
|
||||
* @apiDescription Check the user's ability to delete jobs. Returns whether they are able
|
||||
* to fully delete the job and whether they are able to remove it from
|
||||
* the current space.
|
||||
* Note, this is only for enabling UI controls. A user calling endpoints
|
||||
* directly will still be able to delete or remove the job from a space.
|
||||
*
|
||||
* @apiSchema (body) jobIdsSchema (params) jobTypeSchema
|
||||
* @apiSchema (params) jobTypeSchema
|
||||
* @apiSchema (body) jobIdsSchema
|
||||
* @apiSuccessExample {json} Error-Response:
|
||||
* {
|
||||
* "my_job": {
|
||||
* "canDelete": false,
|
||||
* "canRemoveFromSpace": true
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
router.post(
|
||||
|
@ -298,7 +308,7 @@ export function savedObjectsRoutes(
|
|||
path: '/api/ml/saved_objects/can_delete_job/{jobType}',
|
||||
validate: {
|
||||
params: jobTypeSchema,
|
||||
body: jobIdsSchema,
|
||||
body: canDeleteJobSchema,
|
||||
},
|
||||
options: {
|
||||
tags: ['access:ml:canGetJobs', 'access:ml:canGetDataFrameAnalytics'],
|
||||
|
|
|
@ -22,3 +22,8 @@ export const syncJobObjects = schema.object({ simulate: schema.maybe(schema.bool
|
|||
export const jobTypeSchema = schema.object({
|
||||
jobType: schema.string(),
|
||||
});
|
||||
|
||||
export const canDeleteJobSchema = schema.object({
|
||||
/** List of job IDs. */
|
||||
jobIds: schema.arrayOf(schema.maybe(schema.string())),
|
||||
});
|
||||
|
|
|
@ -180,7 +180,7 @@ export function checksFactory(
|
|||
return jobIds.reduce((results, jobId) => {
|
||||
results[jobId] = {
|
||||
canDelete: false,
|
||||
canUntag: false,
|
||||
canRemoveFromSpace: false,
|
||||
};
|
||||
return results;
|
||||
}, {} as DeleteJobCheckResponse);
|
||||
|
@ -191,7 +191,7 @@ export function checksFactory(
|
|||
return jobIds.reduce((results, jobId) => {
|
||||
results[jobId] = {
|
||||
canDelete: true,
|
||||
canUntag: false,
|
||||
canRemoveFromSpace: false,
|
||||
};
|
||||
return results;
|
||||
}, {} as DeleteJobCheckResponse);
|
||||
|
@ -208,7 +208,7 @@ export function checksFactory(
|
|||
// job saved object not found
|
||||
results[jobId] = {
|
||||
canDelete: false,
|
||||
canUntag: false,
|
||||
canRemoveFromSpace: false,
|
||||
};
|
||||
return results;
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ export function checksFactory(
|
|||
if (canCreateGlobalJobs && isGlobalJob) {
|
||||
results[jobId] = {
|
||||
canDelete: true,
|
||||
canUntag: false,
|
||||
canRemoveFromSpace: false,
|
||||
};
|
||||
return results;
|
||||
}
|
||||
|
@ -229,20 +229,20 @@ export function checksFactory(
|
|||
if (isGlobalJob) {
|
||||
results[jobId] = {
|
||||
canDelete: false,
|
||||
canUntag: false,
|
||||
canRemoveFromSpace: false,
|
||||
};
|
||||
return results;
|
||||
}
|
||||
|
||||
// jobs with are in individual spaces can only be untagged
|
||||
// from current space if the job is in more than 1 space
|
||||
const canUntag = namespaces.length > 1;
|
||||
const canRemoveFromSpace = namespaces.length > 1;
|
||||
|
||||
// job is in individual spaces, user cannot see all of them - untag only, no delete
|
||||
if (namespaces.includes('?')) {
|
||||
results[jobId] = {
|
||||
canDelete: false,
|
||||
canUntag,
|
||||
canRemoveFromSpace,
|
||||
};
|
||||
return results;
|
||||
}
|
||||
|
@ -250,7 +250,7 @@ export function checksFactory(
|
|||
// job is individual spaces, user can see all of them - delete and option to untag
|
||||
results[jobId] = {
|
||||
canDelete: true,
|
||||
canUntag,
|
||||
canRemoveFromSpace,
|
||||
};
|
||||
return results;
|
||||
}, {} as DeleteJobCheckResponse);
|
||||
|
|
|
@ -75,7 +75,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
idSpace1
|
||||
);
|
||||
|
||||
expect(body).to.eql({ [adJobIdSpace12]: { canDelete: false, canUntag: true } });
|
||||
expect(body).to.eql({ [adJobIdSpace12]: { canDelete: false, canRemoveFromSpace: true } });
|
||||
});
|
||||
|
||||
it('job in individual spaces, all spaces user can delete and untag', async () => {
|
||||
|
@ -87,7 +87,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
idSpace1
|
||||
);
|
||||
|
||||
expect(body).to.eql({ [adJobIdSpace12]: { canDelete: true, canUntag: true } });
|
||||
expect(body).to.eql({ [adJobIdSpace12]: { canDelete: true, canRemoveFromSpace: true } });
|
||||
});
|
||||
|
||||
it('job in * space, single space user can not untag or delete', async () => {
|
||||
|
@ -99,7 +99,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
idSpace1
|
||||
);
|
||||
|
||||
expect(body).to.eql({ [adJobIdStarSpace]: { canDelete: false, canUntag: false } });
|
||||
expect(body).to.eql({ [adJobIdStarSpace]: { canDelete: false, canRemoveFromSpace: false } });
|
||||
});
|
||||
|
||||
it('job in * space, all spaces user can delete but not untag', async () => {
|
||||
|
@ -111,7 +111,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
idStarSpace
|
||||
);
|
||||
|
||||
expect(body).to.eql({ [adJobIdStarSpace]: { canDelete: true, canUntag: false } });
|
||||
expect(body).to.eql({ [adJobIdStarSpace]: { canDelete: true, canRemoveFromSpace: false } });
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue