feature/full-breadcrumb-on-task-show (#675)

* show the full breadcrumb on task show page w/ burnettk

* check read permission of process model before displaying in breadcrumb on task show page

* in the breadcrumb if the api returns 401 then just ignore the breadcrumb

* pyl

---------

Co-authored-by: jasquat <jasquat@users.noreply.github.com>
This commit is contained in:
jasquat 2023-11-16 10:05:08 -05:00 committed by GitHub
parent 32f8ca5154
commit 1eed6e2444
6 changed files with 110 additions and 71 deletions

View File

@ -17,6 +17,8 @@ groups:
users: [jason@sartography.com, kevin@sartography.com] users: [jason@sartography.com, kevin@sartography.com]
group2: group2:
users: [dan@sartography.com] users: [dan@sartography.com]
group3:
users: [jon@sartography.com]
permissions: permissions:
admin: admin:
@ -25,7 +27,7 @@ permissions:
uri: /* uri: /*
basic: basic:
groups: [group2] groups: [group2, group3]
actions: [all] actions: [all]
uri: BASIC uri: BASIC

View File

@ -158,7 +158,8 @@ def process_model_show(modified_process_model_identifier: str, include_file_refe
process_model.bpmn_version_control_identifier = current_git_revision process_model.bpmn_version_control_identifier = current_git_revision
available_actions = {} # if the user got here then they can read the process model
available_actions = {"read": {"path": f"/process-models/{modified_process_model_identifier}", "method": "GET"}}
if GitService.check_for_publish_configs(raise_on_missing=False): if GitService.check_for_publish_configs(raise_on_missing=False):
available_actions = { available_actions = {
"publish": {"path": f"/process-model-publish/{modified_process_model_identifier}", "method": "POST"} "publish": {"path": f"/process-model-publish/{modified_process_model_identifier}", "method": "POST"}

View File

@ -4,6 +4,8 @@ import { useEffect, useState } from 'react';
import { modifyProcessIdentifierForPathParam } from '../helpers'; import { modifyProcessIdentifierForPathParam } from '../helpers';
import { import {
HotCrumbItem, HotCrumbItem,
HotCrumbItemArray,
HotCrumbItemObject,
ProcessGroup, ProcessGroup,
ProcessGroupLite, ProcessGroupLite,
ProcessModel, ProcessModel,
@ -29,6 +31,7 @@ export default function ProcessBreadcrumb({ hotCrumbs }: OwnProps) {
entityToExplode as string entityToExplode as string
)}`, )}`,
successCallback: setProcessEntity, successCallback: setProcessEntity,
onUnauthorized: () => {},
}); });
} else if (entityType === 'process-group-id') { } else if (entityType === 'process-group-id') {
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
@ -36,6 +39,7 @@ export default function ProcessBreadcrumb({ hotCrumbs }: OwnProps) {
entityToExplode as string entityToExplode as string
)}`, )}`,
successCallback: setProcessEntity, successCallback: setProcessEntity,
onUnauthorized: () => {},
}); });
} else { } else {
setProcessEntity(entityToExplode as any); setProcessEntity(entityToExplode as any);
@ -47,74 +51,91 @@ export default function ProcessBreadcrumb({ hotCrumbs }: OwnProps) {
} }
}, [setProcessEntity, hotCrumbs]); }, [setProcessEntity, hotCrumbs]);
const checkPermissions = (crumb: HotCrumbItemObject) => {
if (!crumb.checkPermission) {
return true;
}
return (
processEntity &&
'actions' in processEntity &&
processEntity.actions &&
'read' in processEntity.actions
);
};
// eslint-disable-next-line sonarjs/cognitive-complexity // eslint-disable-next-line sonarjs/cognitive-complexity
const hotCrumbElement = () => { const hotCrumbElement = () => {
if (hotCrumbs) { if (hotCrumbs) {
const leadingCrumbLinks = hotCrumbs.map((crumb: any) => { const leadingCrumbLinks = hotCrumbs.map(
if ( (crumb: HotCrumbItemArray | HotCrumbItemObject) => {
'entityToExplode' in crumb && if (
processEntity && 'entityToExplode' in crumb &&
processEntity.parent_groups processEntity &&
) { processEntity.parent_groups &&
const breadcrumbs = processEntity.parent_groups.map( checkPermissions(crumb)
(parentGroup: ProcessGroupLite) => { ) {
const fullUrl = `/process-groups/${modifyProcessIdentifierForPathParam( const breadcrumbs = processEntity.parent_groups.map(
parentGroup.id (parentGroup: ProcessGroupLite) => {
const fullUrl = `/process-groups/${modifyProcessIdentifierForPathParam(
parentGroup.id
)}`;
return (
<BreadcrumbItem key={parentGroup.id} href={fullUrl}>
{parentGroup.display_name}
</BreadcrumbItem>
);
}
);
if (crumb.linkLastItem) {
let apiBase = '/process-groups';
let dataQaTag = '';
if (crumb.entityType.startsWith('process-model')) {
apiBase = '/process-models';
dataQaTag = 'process-model-breadcrumb-link';
}
const fullUrl = `${apiBase}/${modifyProcessIdentifierForPathParam(
processEntity.id
)}`; )}`;
return ( breadcrumbs.push(
<BreadcrumbItem key={parentGroup.id} href={fullUrl}> <BreadcrumbItem
{parentGroup.display_name} key={processEntity.id}
href={fullUrl}
data-qa={dataQaTag}
>
{processEntity.display_name}
</BreadcrumbItem>
);
} else {
breadcrumbs.push(
<BreadcrumbItem key={processEntity.id} isCurrentPage>
{processEntity.display_name}
</BreadcrumbItem> </BreadcrumbItem>
); );
} }
); return breadcrumbs;
if (crumb.linkLastItem) {
let apiBase = '/process-groups';
let dataQaTag = '';
if (crumb.entityType.startsWith('process-model')) {
apiBase = '/process-models';
dataQaTag = 'process-model-breadcrumb-link';
}
const fullUrl = `${apiBase}/${modifyProcessIdentifierForPathParam(
processEntity.id
)}`;
breadcrumbs.push(
<BreadcrumbItem
key={processEntity.id}
href={fullUrl}
data-qa={dataQaTag}
>
{processEntity.display_name}
</BreadcrumbItem>
);
} else {
breadcrumbs.push(
<BreadcrumbItem key={processEntity.id} isCurrentPage>
{processEntity.display_name}
</BreadcrumbItem>
);
} }
return breadcrumbs; if (Array.isArray(crumb)) {
const valueLabel = crumb[0];
const url = crumb[1];
if (!url && valueLabel) {
return (
<BreadcrumbItem isCurrentPage key={valueLabel}>
{valueLabel}
</BreadcrumbItem>
);
}
if (url && valueLabel) {
return (
<BreadcrumbItem key={valueLabel} href={url}>
{valueLabel}
</BreadcrumbItem>
);
}
}
return null;
} }
const valueLabel = crumb[0]; );
const url = crumb[1];
if (!url && valueLabel) {
return (
<BreadcrumbItem isCurrentPage key={valueLabel}>
{valueLabel}
</BreadcrumbItem>
);
}
if (url && valueLabel) {
return (
<BreadcrumbItem key={valueLabel} href={url}>
{valueLabel}
</BreadcrumbItem>
);
}
return null;
});
return <Breadcrumb noTrailingSlash>{leadingCrumbLinks}</Breadcrumb>; return <Breadcrumb noTrailingSlash>{leadingCrumbLinks}</Breadcrumb>;
} }
return null; return null;

View File

@ -6,6 +6,20 @@ import { AbilityContext } from '../contexts/Can';
import { PermissionCheckResponseBody, PermissionsToCheck } from '../interfaces'; import { PermissionCheckResponseBody, PermissionsToCheck } from '../interfaces';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
export const checkPermissions = (
permissionsToCheck: PermissionsToCheck,
successCallback: Function
) => {
if (Object.keys(permissionsToCheck).length !== 0) {
HttpService.makeCallToBackend({
path: `/permissions-check`,
httpMethod: 'POST',
successCallback,
postBody: { requests_to_check: permissionsToCheck },
});
}
};
export const usePermissionFetcher = ( export const usePermissionFetcher = (
permissionsToCheck: PermissionsToCheck permissionsToCheck: PermissionsToCheck
) => { ) => {
@ -37,14 +51,7 @@ export const usePermissionFetcher = (
ability.update(rules); ability.update(rules);
setPermissionsLoaded(true); setPermissionsLoaded(true);
}; };
if (Object.keys(permissionsToCheck).length !== 0) { checkPermissions(permissionsToCheck, processPermissionResult);
HttpService.makeCallToBackend({
path: `/permissions-check`,
httpMethod: 'POST',
successCallback: processPermissionResult,
postBody: { requests_to_check: permissionsToCheck },
});
}
}); });
return { ability, permissionsLoaded }; return { ability, permissionsLoaded };

View File

@ -299,6 +299,7 @@ export interface HotCrumbItemObject {
entityToExplode: ProcessModel | ProcessGroup | string; entityToExplode: ProcessModel | ProcessGroup | string;
entityType: string; entityType: string;
linkLastItem?: boolean; linkLastItem?: boolean;
checkPermission?: boolean;
} }
export type HotCrumbItemArray = [displayValue: string, url?: string]; export type HotCrumbItemArray = [displayValue: string, url?: string];

View File

@ -411,11 +411,18 @@ export default function TaskShow() {
pageElements.push( pageElements.push(
<ProcessBreadcrumb <ProcessBreadcrumb
hotCrumbs={[ hotCrumbs={[
['Process Groups', '/process-groups'],
{
entityToExplode: basicTask.process_model_identifier,
entityType: 'process-model-id',
linkLastItem: true,
checkPermission: true,
},
[ [
`Process Instance Id: ${params.process_instance_id}`, `Process Instance Id: ${basicTask.process_instance_id}`,
`/process-instances/for-me/${modifyProcessIdentifierForPathParam( `/process-instances/for-me/${modifyProcessIdentifierForPathParam(
basicTask.process_model_identifier basicTask.process_model_identifier
)}/${params.process_instance_id}`, )}/${basicTask.process_instance_id}`,
], ],
[`Task: ${basicTask.name_for_display || basicTask.id}`], [`Task: ${basicTask.name_for_display || basicTask.id}`],
]} ]}