From 4c882a84b615f4cec7c05d6191c80604538f6670 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Fri, 9 Dec 2022 17:44:25 -0500 Subject: [PATCH 01/20] Confirm before overwriting file when uploading file with same name --- .../src/routes/ProcessModelShow.tsx | 74 ++++++++++++++++--- 1 file changed, 62 insertions(+), 12 deletions(-) diff --git a/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx b/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx index ef0c65c1..c5765d89 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx @@ -360,27 +360,76 @@ export default function ProcessModelShow() { ); }; + const [fileUploadEvent, setFileUploadEvent] = useState(null); + const [duplicateFilename, setDuplicateFilename] = useState(''); + const [showOverwriteConfirmationPrompt, setShowOverwriteConfirmationPrompt] = + useState(false); + + const doFileUpload = (event: any) => { + event.preventDefault(); + const url = `/process-models/${modifiedProcessModelId}/files`; + const formData = new FormData(); + formData.append('file', filesToUpload[0]); + formData.append('fileName', filesToUpload[0].name); + HttpService.makeCallToBackend({ + path: url, + successCallback: onUploadedCallback, + httpMethod: 'POST', + postBody: formData, + }); + setFilesToUpload(null); + }; + const handleFileUploadCancel = () => { setShowFileUploadModal(false); setFilesToUpload(null); }; + const handleOverwriteFileConfirm = () => { + setShowOverwriteConfirmationPrompt(false); + doFileUpload(fileUploadEvent); + }; + const handleOverwriteFileCancel = () => { + setShowOverwriteConfirmationPrompt(false); + setFilesToUpload(null); + }; + + const confirmOverwriteFileDialog = () => { + return ( + + ); + }; + const displayOverwriteConfirmation = (filename: String) => { + setDuplicateFilename(filename); + setShowOverwriteConfirmationPrompt(true); + }; + + const checkDuplicateFile = (event: any) => { + if (processModel && processModel.files.length > 0) { + for (const file in processModel.files) { + if (processModel.files[file].name === filesToUpload[0].name) { + displayOverwriteConfirmation(processModel.files[file].name); + setFileUploadEvent(event); + } + } + } + }; const handleFileUpload = (event: any) => { if (processModel) { - event.preventDefault(); - const url = `/process-models/${modifiedProcessModelId}/files`; - const formData = new FormData(); - formData.append('file', filesToUpload[0]); - formData.append('fileName', filesToUpload[0].name); - HttpService.makeCallToBackend({ - path: url, - successCallback: onUploadedCallback, - httpMethod: 'POST', - postBody: formData, - }); + checkDuplicateFile(event); } setShowFileUploadModal(false); - setFilesToUpload(null); }; const fileUploadModal = () => { @@ -548,6 +597,7 @@ export default function ProcessModelShow() { return ( <> {fileUploadModal()} + {confirmOverwriteFileDialog()} Date: Mon, 12 Dec 2022 12:21:37 -0500 Subject: [PATCH 02/20] split group task tables by group and created component for group tables --- .../src/spiffworkflow_backend/api.yml | 22 ++ .../config/permissions/development.yml | 1 + .../routes/process_api_blueprint.py | 25 ++- .../components/TasksForMyOpenProcesses.tsx | 159 +------------- .../src/components/TasksTable.tsx | 200 ++++++++++++++++++ .../src/components/TasksWaitingForMe.tsx | 154 +------------- .../components/TasksWaitingForMyGroups.tsx | 169 +++------------ spiffworkflow-frontend/src/helpers.tsx | 12 +- spiffworkflow-frontend/src/interfaces.ts | 10 + 9 files changed, 304 insertions(+), 448 deletions(-) create mode 100644 spiffworkflow-frontend/src/components/TasksTable.tsx diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml index 764ba543..2836dac2 100755 --- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml @@ -1082,6 +1082,12 @@ paths: /tasks/for-my-groups: parameters: + - name: group_identifier + in: query + required: false + description: The identifier of the group to get the tasks for + schema: + type: string - name: page in: query required: false @@ -1109,6 +1115,22 @@ paths: items: $ref: "#/components/schemas/Task" + /tasks/user-groups: + get: + tags: + - Process Instances + operationId: spiffworkflow_backend.routes.process_api_blueprint.task_list_user_groups + summary: Group identifiers for current logged in user + responses: + "200": + description: list of user groups + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Task" + /task-data/{modified_process_model_identifier}/{process_instance_id}: parameters: - name: modified_process_model_identifier diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml index 419c925f..1f38e02b 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml @@ -50,6 +50,7 @@ groups: fin, fin1, harmeet, + jason, sasha, manuchehr, lead, diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index c29cf214..2f194af6 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -1301,7 +1301,6 @@ def task_list_for_my_open_processes( def task_list_for_me(page: int = 1, per_page: int = 100) -> flask.wrappers.Response: - """Task_list_for_processes_started_by_others.""" return get_tasks( processes_started_by_user=False, has_lane_assignment_id=False, @@ -1311,10 +1310,18 @@ def task_list_for_me(page: int = 1, per_page: int = 100) -> flask.wrappers.Respo def task_list_for_my_groups( + group_identifier: str = None, page: int = 1, per_page: int = 100 ) -> flask.wrappers.Response: - """Task_list_for_processes_started_by_others.""" - return get_tasks(processes_started_by_user=False, page=page, per_page=per_page) + return get_tasks(group_identifier=group_identifier, processes_started_by_user=False, page=page, per_page=per_page) + + +def task_list_user_groups( +) -> flask.wrappers.Response: + groups = g.user.groups + # TODO: filter out the default group and have a way to know what is the default group + group_identifiers = [i.identifier for i in groups if i.identifier != 'everybody'] + return make_response(jsonify(sorted(group_identifiers)), 200) def get_tasks( @@ -1322,6 +1329,7 @@ def get_tasks( has_lane_assignment_id: bool = True, page: int = 1, per_page: int = 100, + group_identifier: str = None, ) -> flask.wrappers.Response: """Get_tasks.""" user_id = g.user.id @@ -1358,9 +1366,14 @@ def get_tasks( ), ) if has_lane_assignment_id: - active_tasks_query = active_tasks_query.filter( - ActiveTaskModel.lane_assignment_id.is_not(None) # type: ignore - ) + if group_identifier: + active_tasks_query = active_tasks_query.filter( + GroupModel.identifier == group_identifier + ) + else: + active_tasks_query = active_tasks_query.filter( + ActiveTaskModel.lane_assignment_id.is_not(None) # type: ignore + ) else: active_tasks_query = active_tasks_query.filter(ActiveTaskModel.lane_assignment_id.is_(None)) # type: ignore diff --git a/spiffworkflow-frontend/src/components/TasksForMyOpenProcesses.tsx b/spiffworkflow-frontend/src/components/TasksForMyOpenProcesses.tsx index deb2030e..7ee7edec 100644 --- a/spiffworkflow-frontend/src/components/TasksForMyOpenProcesses.tsx +++ b/spiffworkflow-frontend/src/components/TasksForMyOpenProcesses.tsx @@ -1,156 +1,17 @@ -import { useEffect, useState } from 'react'; -// @ts-ignore -import { Button, Table } from '@carbon/react'; -import { Link, useSearchParams } from 'react-router-dom'; -import PaginationForTable from './PaginationForTable'; -import { - convertSecondsToFormattedDateTime, - getPageInfoFromSearchParams, - modifyProcessIdentifierForPathParam, - refreshAtInterval, -} from '../helpers'; -import HttpService from '../services/HttpService'; -import { PaginationObject } from '../interfaces'; -import TableCellWithTimeAgoInWords from './TableCellWithTimeAgoInWords'; +import TasksTable from './TasksTable'; -const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5; const paginationQueryParamPrefix = 'tasks_for_my_open_processes'; -const REFRESH_INTERVAL = 5; -const REFRESH_TIMEOUT = 600; export default function MyOpenProcesses() { - const [searchParams] = useSearchParams(); - const [tasks, setTasks] = useState([]); - const [pagination, setPagination] = useState(null); - - useEffect(() => { - const getTasks = () => { - const { page, perPage } = getPageInfoFromSearchParams( - searchParams, - PER_PAGE_FOR_TASKS_ON_HOME_PAGE, - undefined, - paginationQueryParamPrefix - ); - const setTasksFromResult = (result: any) => { - setTasks(result.results); - setPagination(result.pagination); - }; - HttpService.makeCallToBackend({ - path: `/tasks/for-my-open-processes?per_page=${perPage}&page=${page}`, - successCallback: setTasksFromResult, - }); - }; - getTasks(); - refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks); - }, [searchParams]); - - const buildTable = () => { - const rows = tasks.map((row) => { - const rowToUse = row as any; - const taskUrl = `/tasks/${rowToUse.process_instance_id}/${rowToUse.task_id}`; - const modifiedProcessModelIdentifier = - modifyProcessIdentifierForPathParam(rowToUse.process_model_identifier); - return ( - - - - {rowToUse.process_instance_id} - - - - - {rowToUse.process_model_display_name} - - - - {rowToUse.task_title} - - {rowToUse.group_identifier || '-'} - - {convertSecondsToFormattedDateTime( - rowToUse.created_at_in_seconds - ) || '-'} - - - - - - - ); - }); - return ( - - - - - - - - - - - - - {rows} -
IdProcessTaskWaiting ForDate StartedLast UpdatedActions
- ); - }; - - const tasksComponent = () => { - if (pagination && pagination.total < 1) { - return ( -

- There are no tasks for processes you started at this time. -

- ); - } - const { page, perPage } = getPageInfoFromSearchParams( - searchParams, - PER_PAGE_FOR_TASKS_ON_HOME_PAGE, - undefined, - paginationQueryParamPrefix - ); - return ( - - ); - }; - return ( - <> -

My open instances

-

- These tasks are for processes you started which are not complete. You - may not have an action to take at this time. See below for tasks waiting - on you. -

- {tasksComponent()} - + ); } diff --git a/spiffworkflow-frontend/src/components/TasksTable.tsx b/spiffworkflow-frontend/src/components/TasksTable.tsx new file mode 100644 index 00000000..1c4d15e9 --- /dev/null +++ b/spiffworkflow-frontend/src/components/TasksTable.tsx @@ -0,0 +1,200 @@ +import { useEffect, useState } from 'react'; +// @ts-ignore +import { Button, Table } from '@carbon/react'; +import { Link, useSearchParams } from 'react-router-dom'; +import PaginationForTable from './PaginationForTable'; +import { + convertSecondsToFormattedDateTime, + getPageInfoFromSearchParams, + modifyProcessIdentifierForPathParam, + refreshAtInterval, +} from '../helpers'; +import HttpService from '../services/HttpService'; +import { PaginationObject, ProcessInstanceTask } from '../interfaces'; +import TableCellWithTimeAgoInWords from './TableCellWithTimeAgoInWords'; + +const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5; +const REFRESH_INTERVAL = 5; +const REFRESH_TIMEOUT = 600; + +type OwnProps = { + apiPath: string; + tableTitle: string; + tableDescription: string; + additionalParams?: string; + paginationQueryParamPrefix?: string; + paginationClassName?: string; + autoReload?: boolean; + showStartedBy?: boolean; + showWaitingOn?: boolean; +}; + +export default function TasksTable({ + apiPath, + tableTitle, + tableDescription, + additionalParams, + paginationQueryParamPrefix, + paginationClassName, + autoReload = false, + showStartedBy = true, + showWaitingOn = true, +}: OwnProps) { + const [searchParams] = useSearchParams(); + const [tasks, setTasks] = useState(null); + const [pagination, setPagination] = useState(null); + + useEffect(() => { + const getTasks = () => { + const { page, perPage } = getPageInfoFromSearchParams( + searchParams, + PER_PAGE_FOR_TASKS_ON_HOME_PAGE, + undefined, + paginationQueryParamPrefix + ); + const setTasksFromResult = (result: any) => { + setTasks(result.results); + setPagination(result.pagination); + }; + let params = `?per_page=${perPage}&page=${page}`; + if (additionalParams) { + params = `${params}&${additionalParams}`; + } + HttpService.makeCallToBackend({ + path: `${apiPath}${params}`, + successCallback: setTasksFromResult, + }); + }; + getTasks(); + if (autoReload) { + refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks); + } + }, [ + searchParams, + additionalParams, + apiPath, + paginationQueryParamPrefix, + autoReload, + ]); + + const buildTable = () => { + if (!tasks) { + return null; + } + const rows = tasks.map((row) => { + const rowToUse = row as any; + const taskUrl = `/tasks/${rowToUse.process_instance_id}/${rowToUse.task_id}`; + const modifiedProcessModelIdentifier = + modifyProcessIdentifierForPathParam(rowToUse.process_model_identifier); + return ( + + + + {rowToUse.process_instance_id} + + + + + {rowToUse.process_model_display_name} + + + + {rowToUse.task_title} + + {showStartedBy ? {rowToUse.username} : ''} + {showWaitingOn ? {rowToUse.group_identifier || '-'} : ''} + + {convertSecondsToFormattedDateTime( + rowToUse.created_at_in_seconds + ) || '-'} + + + + + + + ); + }); + let tableHeaders = ['Id', 'Process', 'Task']; + if (showStartedBy) { + tableHeaders.push('Started By'); + } + if (showWaitingOn) { + tableHeaders.push('Waiting For'); + } + tableHeaders = tableHeaders.concat([ + 'Date Started', + 'Last Updated', + 'Actions', + ]); + return ( + + + + {tableHeaders.map((tableHeader: string) => { + return ; + })} + + + {rows} +
{tableHeader}
+ ); + }; + + const tasksComponent = () => { + if (pagination && pagination.total < 1) { + return ( +

+ Your groups have no task assignments at this time. +

+ ); + } + const { page, perPage } = getPageInfoFromSearchParams( + searchParams, + PER_PAGE_FOR_TASKS_ON_HOME_PAGE, + undefined, + paginationQueryParamPrefix + ); + return ( + + ); + }; + + if (tasks) { + return ( + <> +

{tableTitle}

+

{tableDescription}

+ {tasksComponent()} + + ); + } + return null; +} diff --git a/spiffworkflow-frontend/src/components/TasksWaitingForMe.tsx b/spiffworkflow-frontend/src/components/TasksWaitingForMe.tsx index 7d06b7a3..e253afd6 100644 --- a/spiffworkflow-frontend/src/components/TasksWaitingForMe.tsx +++ b/spiffworkflow-frontend/src/components/TasksWaitingForMe.tsx @@ -1,149 +1,15 @@ -import { useEffect, useState } from 'react'; -// @ts-ignore -import { Button, Table } from '@carbon/react'; -import { Link, useSearchParams } from 'react-router-dom'; -import PaginationForTable from './PaginationForTable'; -import { - convertSecondsToFormattedDateTime, - getPageInfoFromSearchParams, - modifyProcessIdentifierForPathParam, -} from '../helpers'; -import HttpService from '../services/HttpService'; -import { PaginationObject } from '../interfaces'; -import TableCellWithTimeAgoInWords from './TableCellWithTimeAgoInWords'; - -const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5; +import TasksTable from './TasksTable'; export default function TasksWaitingForMe() { - const [searchParams] = useSearchParams(); - const [tasks, setTasks] = useState([]); - const [pagination, setPagination] = useState(null); - - useEffect(() => { - const { page, perPage } = getPageInfoFromSearchParams( - searchParams, - PER_PAGE_FOR_TASKS_ON_HOME_PAGE, - undefined, - 'tasks_waiting_for_me' - ); - const setTasksFromResult = (result: any) => { - setTasks(result.results); - setPagination(result.pagination); - }; - HttpService.makeCallToBackend({ - path: `/tasks/for-me?per_page=${perPage}&page=${page}`, - successCallback: setTasksFromResult, - }); - }, [searchParams]); - - const buildTable = () => { - const rows = tasks.map((row) => { - const rowToUse = row as any; - const taskUrl = `/tasks/${rowToUse.process_instance_id}/${rowToUse.task_id}`; - const modifiedProcessModelIdentifier = - modifyProcessIdentifierForPathParam(rowToUse.process_model_identifier); - return ( - - - - {rowToUse.process_instance_id} - - - - - {rowToUse.process_model_display_name} - - - - {rowToUse.task_title} - - {rowToUse.username} - {rowToUse.group_identifier || '-'} - - {convertSecondsToFormattedDateTime( - rowToUse.created_at_in_seconds - ) || '-'} - - - - - - - ); - }); - return ( - - - - - - - - - - - - - - {rows} -
IdProcessTaskStarted ByWaiting ForDate StartedLast UpdatedActions
- ); - }; - - const tasksComponent = () => { - if (pagination && pagination.total < 1) { - return ( -

- You have no task assignments at this time. -

- ); - } - const { page, perPage } = getPageInfoFromSearchParams( - searchParams, - PER_PAGE_FOR_TASKS_ON_HOME_PAGE, - undefined, - 'tasks_waiting_for_me' - ); - return ( - - ); - }; - return ( - <> -

Tasks waiting for me

-

- These processes are waiting on you to complete the next task. All are - processes created by others that are now actionable by you. -

- {tasksComponent()} - + ); } diff --git a/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx b/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx index 565cd4a5..93d21640 100644 --- a/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx +++ b/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx @@ -1,156 +1,39 @@ import { useEffect, useState } from 'react'; -// @ts-ignore -import { Button, Table } from '@carbon/react'; -import { Link, useSearchParams } from 'react-router-dom'; -import PaginationForTable from './PaginationForTable'; -import { - convertSecondsToFormattedDateTime, - getPageInfoFromSearchParams, - modifyProcessIdentifierForPathParam, - refreshAtInterval, -} from '../helpers'; import HttpService from '../services/HttpService'; -import { PaginationObject } from '../interfaces'; -import TableCellWithTimeAgoInWords from './TableCellWithTimeAgoInWords'; - -const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5; -const paginationQueryParamPrefix = 'tasks_waiting_for_my_groups'; -const REFRESH_INTERVAL = 5; -const REFRESH_TIMEOUT = 600; +import TasksTable from './TasksTable'; export default function TasksWaitingForMyGroups() { - const [searchParams] = useSearchParams(); - const [tasks, setTasks] = useState([]); - const [pagination, setPagination] = useState(null); + const [userGroups, setUserGroups] = useState(null); useEffect(() => { - const getTasks = () => { - const { page, perPage } = getPageInfoFromSearchParams( - searchParams, - PER_PAGE_FOR_TASKS_ON_HOME_PAGE, - undefined, - paginationQueryParamPrefix - ); - const setTasksFromResult = (result: any) => { - setTasks(result.results); - setPagination(result.pagination); - }; - HttpService.makeCallToBackend({ - path: `/tasks/for-my-groups?per_page=${perPage}&page=${page}`, - successCallback: setTasksFromResult, - }); - }; - getTasks(); - refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks); - }, [searchParams]); + HttpService.makeCallToBackend({ + path: `/tasks/user-groups`, + successCallback: setUserGroups, + }); + }, [setUserGroups]); + const tableComponents = () => { + if (!userGroups) { + return null; + } - const buildTable = () => { - const rows = tasks.map((row) => { - const rowToUse = row as any; - const taskUrl = `/tasks/${rowToUse.process_instance_id}/${rowToUse.task_id}`; - const modifiedProcessModelIdentifier = - modifyProcessIdentifierForPathParam(rowToUse.process_model_identifier); + return userGroups.map((userGroup: string) => { return ( - - - - {rowToUse.process_instance_id} - - - - - {rowToUse.process_model_display_name} - - - - {rowToUse.task_title} - - {rowToUse.username} - {rowToUse.group_identifier || '-'} - - {convertSecondsToFormattedDateTime( - rowToUse.created_at_in_seconds - ) || '-'} - - - - - - + ); }); - return ( - - - - - - - - - - - - - - {rows} -
IdProcessTaskStarted ByWaiting ForDate StartedLast UpdatedActions
- ); }; - const tasksComponent = () => { - if (pagination && pagination.total < 1) { - return ( -

- Your groups have no task assignments at this time. -

- ); - } - const { page, perPage } = getPageInfoFromSearchParams( - searchParams, - PER_PAGE_FOR_TASKS_ON_HOME_PAGE, - undefined, - paginationQueryParamPrefix - ); - return ( - - ); - }; - - return ( - <> -

Tasks waiting for my groups

-

- This is a list of tasks for groups you belong to that can be completed - by any member of the group. -

- {tasksComponent()} - - ); + if (userGroups) { + return <>{tableComponents()}; + } + return null; } diff --git a/spiffworkflow-frontend/src/helpers.tsx b/spiffworkflow-frontend/src/helpers.tsx index 6781ada9..fcc3371b 100644 --- a/spiffworkflow-frontend/src/helpers.tsx +++ b/spiffworkflow-frontend/src/helpers.tsx @@ -203,10 +203,10 @@ export const refreshAtInterval = ( timeout: number, func: Function ) => { - const intervalRef = setInterval(() => func(), interval * 1000); - const timeoutRef = setTimeout( - () => clearInterval(intervalRef), - timeout * 1000 - ); - return [intervalRef, timeoutRef]; + // const intervalRef = setInterval(() => func(), interval * 1000); + // const timeoutRef = setTimeout( + // () => clearInterval(intervalRef), + // timeout * 1000 + // ); + // return [intervalRef, timeoutRef]; }; diff --git a/spiffworkflow-frontend/src/interfaces.ts b/spiffworkflow-frontend/src/interfaces.ts index 079e4cdc..cc3180e5 100644 --- a/spiffworkflow-frontend/src/interfaces.ts +++ b/spiffworkflow-frontend/src/interfaces.ts @@ -11,6 +11,16 @@ export interface RecentProcessModel { processModelDisplayName: string; } +export interface ProcessInstanceTask { + id: number; + process_model_display_name: string; + process_model_identifier: string; + task_title: string; + lane_assignment_id: string; + process_instance_status: number; + updated_at_in_seconds: number; +} + export interface ProcessReference { name: string; // The process or decision Display name. identifier: string; // The unique id of the process From d9765220bd62b6f653d19485a0d21da1b70d0ea8 Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 12 Dec 2022 12:29:36 -0500 Subject: [PATCH 03/20] pyl --- .../routes/process_api_blueprint.py | 20 ++++++++++++------- .../unit/test_git_service.py | 8 ++++++-- spiffworkflow-frontend/src/helpers.tsx | 12 +++++------ 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index 2f194af6..06c23cf5 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -1301,6 +1301,7 @@ def task_list_for_my_open_processes( def task_list_for_me(page: int = 1, per_page: int = 100) -> flask.wrappers.Response: + """Task_list_for_me.""" return get_tasks( processes_started_by_user=False, has_lane_assignment_id=False, @@ -1310,17 +1311,22 @@ def task_list_for_me(page: int = 1, per_page: int = 100) -> flask.wrappers.Respo def task_list_for_my_groups( - group_identifier: str = None, - page: int = 1, per_page: int = 100 + group_identifier: Optional[str] = None, page: int = 1, per_page: int = 100 ) -> flask.wrappers.Response: - return get_tasks(group_identifier=group_identifier, processes_started_by_user=False, page=page, per_page=per_page) + """Task_list_for_my_groups.""" + return get_tasks( + group_identifier=group_identifier, + processes_started_by_user=False, + page=page, + per_page=per_page, + ) -def task_list_user_groups( -) -> flask.wrappers.Response: +def task_list_user_groups() -> flask.wrappers.Response: + """Task_list_user_groups.""" groups = g.user.groups # TODO: filter out the default group and have a way to know what is the default group - group_identifiers = [i.identifier for i in groups if i.identifier != 'everybody'] + group_identifiers = [i.identifier for i in groups if i.identifier != "everybody"] return make_response(jsonify(sorted(group_identifiers)), 200) @@ -1329,7 +1335,7 @@ def get_tasks( has_lane_assignment_id: bool = True, page: int = 1, per_page: int = 100, - group_identifier: str = None, + group_identifier: Optional[str] = None, ) -> flask.wrappers.Response: """Get_tasks.""" user_id = g.user.id diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_git_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_git_service.py index ad3c814f..ed1e24e1 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_git_service.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_git_service.py @@ -7,6 +7,7 @@ from spiffworkflow_backend.services.git_service import GitService class TestGitService(BaseTest): + """TestGitService.""" def test_strips_output_of_stdout_from_command( self, @@ -14,5 +15,8 @@ class TestGitService(BaseTest): client: FlaskClient, with_db_and_bpmn_file_cleanup: None, ) -> None: - output = GitService.run_shell_command_to_get_stdout(["echo", ' This output should not end in space or newline \n']) - assert output == 'This output should not end in space or newline' + """Test_strips_output_of_stdout_from_command.""" + output = GitService.run_shell_command_to_get_stdout( + ["echo", " This output should not end in space or newline \n"] + ) + assert output == "This output should not end in space or newline" diff --git a/spiffworkflow-frontend/src/helpers.tsx b/spiffworkflow-frontend/src/helpers.tsx index fcc3371b..6781ada9 100644 --- a/spiffworkflow-frontend/src/helpers.tsx +++ b/spiffworkflow-frontend/src/helpers.tsx @@ -203,10 +203,10 @@ export const refreshAtInterval = ( timeout: number, func: Function ) => { - // const intervalRef = setInterval(() => func(), interval * 1000); - // const timeoutRef = setTimeout( - // () => clearInterval(intervalRef), - // timeout * 1000 - // ); - // return [intervalRef, timeoutRef]; + const intervalRef = setInterval(() => func(), interval * 1000); + const timeoutRef = setTimeout( + () => clearInterval(intervalRef), + timeout * 1000 + ); + return [intervalRef, timeoutRef]; }; From 0bbe67971fc3b70a6a07cba00180243b8cd6cdbf Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 12 Dec 2022 13:19:27 -0500 Subject: [PATCH 04/20] updated group api so it is not under tasks --- spiffworkflow-backend/src/spiffworkflow_backend/api.yml | 4 ++-- .../spiffworkflow_backend/config/permissions/development.yml | 5 +++++ .../spiffworkflow_backend/routes/process_api_blueprint.py | 4 ++-- .../src/components/TasksWaitingForMyGroups.tsx | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml index 2836dac2..d6066093 100755 --- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml @@ -1115,11 +1115,11 @@ paths: items: $ref: "#/components/schemas/Task" - /tasks/user-groups: + /user-groups/for-current-user: get: tags: - Process Instances - operationId: spiffworkflow_backend.routes.process_api_blueprint.task_list_user_groups + operationId: spiffworkflow_backend.routes.process_api_blueprint.user_groups_for_current_user summary: Group identifiers for current logged in user responses: "200": diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml index 1f38e02b..737c1a70 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml @@ -81,6 +81,11 @@ permissions: users: [] allowed_permissions: [read] uri: /v1.0/service-tasks + user-groups-for-current-user: + groups: [everybody] + users: [] + allowed_permissions: [read] + uri: /v1.0/user-groups/for-current-user # read all for everybody diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index 06c23cf5..19f38934 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -1322,8 +1322,8 @@ def task_list_for_my_groups( ) -def task_list_user_groups() -> flask.wrappers.Response: - """Task_list_user_groups.""" +def user_groups_for_current_user() -> flask.wrappers.Response: + """User_groups_for_current_user.""" groups = g.user.groups # TODO: filter out the default group and have a way to know what is the default group group_identifiers = [i.identifier for i in groups if i.identifier != "everybody"] diff --git a/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx b/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx index 93d21640..99616447 100644 --- a/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx +++ b/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx @@ -7,7 +7,7 @@ export default function TasksWaitingForMyGroups() { useEffect(() => { HttpService.makeCallToBackend({ - path: `/tasks/user-groups`, + path: `/user-groups/for-current-user`, successCallback: setUserGroups, }); }, [setUserGroups]); From 7153045e663c30faf5901460450542a224a18027 Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 12 Dec 2022 13:41:42 -0500 Subject: [PATCH 05/20] split out completed instances by group as well --- .../src/spiffworkflow_backend/api.yml | 6 +++ .../routes/process_api_blueprint.py | 15 ++++-- .../components/ProcessInstanceListTable.tsx | 7 +++ .../src/components/TasksTable.tsx | 2 +- .../components/TasksWaitingForMyGroups.tsx | 1 + .../src/routes/CompletedInstances.tsx | 53 ++++++++++++++----- 6 files changed, 66 insertions(+), 18 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml index d6066093..d14a514b 100755 --- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml @@ -595,6 +595,12 @@ paths: description: Specifies the identifier of a report to use, if any schema: type: integer + - name: group_identifier + in: query + required: false + description: The identifier of the group to get the process instances for + schema: + type: string get: operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_list summary: Returns a list of process instances for a given process model diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index 19f38934..895f8e1b 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -821,6 +821,7 @@ def process_instance_list( user_filter: Optional[bool] = False, report_identifier: Optional[str] = None, report_id: Optional[int] = None, + group_identifier: Optional[str] = None, ) -> flask.wrappers.Response: """Process_instance_list.""" process_instance_report = ProcessInstanceReportService.report_with_identifier( @@ -960,10 +961,16 @@ def process_instance_list( process_instance_query = process_instance_query.filter( SpiffLoggingModel.spiff_step == SpiffStepDetailsModel.spiff_step ) - process_instance_query = process_instance_query.join( - GroupModel, - GroupModel.id == SpiffStepDetailsModel.lane_assignment_id, - ) + if (group_identifier): + process_instance_query = process_instance_query.join( + GroupModel, + GroupModel.identifier == group_identifier, + ) + else: + process_instance_query = process_instance_query.join( + GroupModel, + GroupModel.id == SpiffStepDetailsModel.lane_assignment_id, + ) process_instance_query = process_instance_query.join( UserGroupAssignmentModel, UserGroupAssignmentModel.group_id == GroupModel.id, diff --git a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx index 98b76df3..5e62fcf0 100644 --- a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx +++ b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx @@ -79,6 +79,7 @@ type OwnProps = { textToShowIfEmpty?: string; paginationClassName?: string; autoReload?: boolean; + additionalParams?: string; }; interface dateParameters { @@ -90,6 +91,7 @@ export default function ProcessInstanceListTable({ processModelFullIdentifier, paginationQueryParamPrefix, perPageOptions, + additionalParams, showReports = true, reportIdentifier, textToShowIfEmpty, @@ -253,6 +255,10 @@ export default function ProcessInstanceListTable({ } ); + if (additionalParams) { + queryParamString += `&${additionalParams}`; + } + HttpService.makeCallToBackend({ path: `/process-instances?${queryParamString}`, successCallback: setProcessInstancesFromResult, @@ -315,6 +321,7 @@ export default function ProcessInstanceListTable({ processModelFullIdentifier, perPageOptions, reportIdentifier, + additionalParams, ]); // This sets the filter data using the saved reports returned from the initial instance_list query. diff --git a/spiffworkflow-frontend/src/components/TasksTable.tsx b/spiffworkflow-frontend/src/components/TasksTable.tsx index 1c4d15e9..0c657016 100644 --- a/spiffworkflow-frontend/src/components/TasksTable.tsx +++ b/spiffworkflow-frontend/src/components/TasksTable.tsx @@ -58,7 +58,7 @@ export default function TasksTable({ }; let params = `?per_page=${perPage}&page=${page}`; if (additionalParams) { - params = `${params}&${additionalParams}`; + params += `&${additionalParams}`; } HttpService.makeCallToBackend({ path: `${apiPath}${params}`, diff --git a/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx b/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx index 99616447..948aee6c 100644 --- a/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx +++ b/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx @@ -11,6 +11,7 @@ export default function TasksWaitingForMyGroups() { successCallback: setUserGroups, }); }, [setUserGroups]); + const tableComponents = () => { if (!userGroups) { return null; diff --git a/spiffworkflow-frontend/src/routes/CompletedInstances.tsx b/spiffworkflow-frontend/src/routes/CompletedInstances.tsx index f97bb5d5..4583d027 100644 --- a/spiffworkflow-frontend/src/routes/CompletedInstances.tsx +++ b/spiffworkflow-frontend/src/routes/CompletedInstances.tsx @@ -1,6 +1,45 @@ +import { useEffect, useState } from 'react'; import ProcessInstanceListTable from '../components/ProcessInstanceListTable'; +import HttpService from '../services/HttpService'; export default function CompletedInstances() { + const [userGroups, setUserGroups] = useState(null); + + useEffect(() => { + HttpService.makeCallToBackend({ + path: `/user-groups/for-current-user`, + successCallback: setUserGroups, + }); + }, [setUserGroups]); + + const groupTableComponents = () => { + if (!userGroups) { + return null; + } + + return userGroups.map((userGroup: string) => { + return ( + <> +

Tasks completed by {userGroup} group

+

+ This is a list of instances with tasks that were completed by the{' '} + {userGroup} group. +

+ + + ); + }); + }; + return ( <>

My completed instances

@@ -30,19 +69,7 @@ export default function CompletedInstances() { textToShowIfEmpty="You have no completed tasks at this time." paginationClassName="with-large-bottom-margin" /> -

Tasks completed by my groups

-

- This is a list of instances with tasks that were completed by groups you - belong to. -

- + {groupTableComponents()} ); } From 088826e433d41fd0cdbd286aef0dbdc696f6a2d7 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Tue, 13 Dec 2022 08:52:02 -0500 Subject: [PATCH 06/20] Don't show delete button for primary file --- .../src/components/ReactDiagramEditor.tsx | 4 +- .../src/routes/ProcessModelEditDiagram.tsx | 1 + .../src/routes/ProcessModelShow.tsx | 40 ++++++++++--------- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx b/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx index 11839e9c..30dc0d89 100644 --- a/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx +++ b/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx @@ -68,6 +68,7 @@ type OwnProps = { completedTasksBpmnIds?: string[] | null; saveDiagram?: (..._args: any[]) => any; onDeleteFile?: (..._args: any[]) => any; + isPrimaryFile?: boolean; onSetPrimaryFile?: (..._args: any[]) => any; diagramXML?: string | null; fileName?: string; @@ -92,6 +93,7 @@ export default function ReactDiagramEditor({ completedTasksBpmnIds, saveDiagram, onDeleteFile, + isPrimaryFile, onSetPrimaryFile, diagramXML, fileName, @@ -549,7 +551,7 @@ export default function ReactDiagramEditor({ a={targetUris.processModelFileShowPath} ability={ability} > - {fileName && ( + {fileName && !isPrimaryFile && ( ); - elements.push( - - { - onDeleteFile(processModelFile.name); - }} - confirmButtonLabel="Delete" - /> - - ); + if (!isPrimaryBpmnFile) { + elements.push( + + { + onDeleteFile(processModelFile.name); + }} + confirmButtonLabel="Delete" + /> + + ); + } if (processModelFile.name.match(/\.bpmn$/) && !isPrimaryBpmnFile) { elements.push( From bdb622232dfd9208f2e0eac71f5ff363f716752a Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Tue, 13 Dec 2022 14:20:53 -0500 Subject: [PATCH 07/20] Add a message when file is saved. --- .../src/routes/ProcessModelEditDiagram.tsx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx index 1a5c751f..c4be154c 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx @@ -25,6 +25,7 @@ import { ProcessReference, } from '../interfaces'; import ProcessSearch from '../components/ProcessSearch'; +import { Notification } from '../components/Notification'; export default function ProcessModelEditDiagram() { const [showFileNameEditor, setShowFileNameEditor] = useState(false); @@ -157,6 +158,8 @@ export default function ProcessModelEditDiagram() { } }; + const [displaySaveFileMessage, setDisplaySaveFileMessage] = + useState(false); const saveDiagram = (bpmnXML: any, fileName = params.file_name) => { setErrorMessage(null); setBpmnXmlForDiagramRendering(bpmnXML); @@ -192,6 +195,7 @@ export default function ProcessModelEditDiagram() { // after saving the file, make sure we null out newFileName // so it does not get used over the params setNewFileName(''); + setDisplaySaveFileMessage(true); }; const onDeleteFile = (fileName = params.file_name) => { @@ -836,6 +840,20 @@ export default function ProcessModelEditDiagram() { ); }; + const saveFileMessage = () => { + if (displaySaveFileMessage) { + return ( + setDisplaySaveFileMessage(false)} + > + Changes to the file were saved. + + ); + } + return null; + }; + // if a file name is not given then this is a new model and the ReactDiagramEditor component will handle it if ((bpmnXmlForDiagramRendering || !params.file_name) && processModel) { const processModelFileName = processModelFile ? processModelFile.name : ''; @@ -856,6 +874,7 @@ export default function ProcessModelEditDiagram() { Process Model File{processModelFile ? ': ' : ''} {processModelFileName} + {saveFileMessage()} {appropriateEditor()} {newFileNameBox()} {scriptEditor()} From c2a9a8d22dd43b854398fd35d35a938e49a9aaad Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Tue, 13 Dec 2022 15:57:46 -0500 Subject: [PATCH 08/20] use forEach --- spiffworkflow-frontend/src/routes/ProcessModelShow.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx b/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx index c5765d89..dbe91409 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx @@ -416,12 +416,12 @@ export default function ProcessModelShow() { const checkDuplicateFile = (event: any) => { if (processModel && processModel.files.length > 0) { - for (const file in processModel.files) { - if (processModel.files[file].name === filesToUpload[0].name) { - displayOverwriteConfirmation(processModel.files[file].name); + processModel.files.forEach((file) => { + if (file.name === filesToUpload[0].name) { + displayOverwriteConfirmation(file.name); setFileUploadEvent(event); } - } + }); } }; From 662a1ec5d6c71da838a5db8f074a189ff1d0024a Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 16 Dec 2022 11:39:07 -0500 Subject: [PATCH 09/20] added new api endpoint to get task-info so users with access to process instances can see the tasks but not the data --- flask-bpmn/src/flask_bpmn/api/api_error.py | 16 +++--- .../src/spiffworkflow_backend/api.yml | 51 ++++++++++++++++++- .../config/permissions/development.yml | 12 ++--- .../models/spiff_logging.py | 2 +- .../routes/process_api_blueprint.py | 38 +++++++++++++- .../src/hooks/UriListForPermissions.tsx | 3 +- .../src/routes/ProcessInstanceShow.tsx | 21 ++++++-- .../src/routes/TaskShow.tsx | 4 +- 8 files changed, 122 insertions(+), 25 deletions(-) diff --git a/flask-bpmn/src/flask_bpmn/api/api_error.py b/flask-bpmn/src/flask_bpmn/api/api_error.py index eb390abe..c782c2d3 100644 --- a/flask-bpmn/src/flask_bpmn/api/api_error.py +++ b/flask-bpmn/src/flask_bpmn/api/api_error.py @@ -170,15 +170,17 @@ def set_user_sentry_context() -> None: def handle_exception(exception: Exception) -> flask.wrappers.Response: """Handles unexpected exceptions.""" set_user_sentry_context() - id = capture_exception(exception) - organization_slug = current_app.config.get("SENTRY_ORGANIZATION_SLUG") - project_slug = current_app.config.get("SENTRY_PROJECT_SLUG") sentry_link = None - if organization_slug and project_slug: - sentry_link = ( - f"https://sentry.io/{organization_slug}/{project_slug}/events/{id}" - ) + if not isinstance(exception, ApiError) or exception.error_code != "invalid_token": + id = capture_exception(exception) + + organization_slug = current_app.config.get("SENTRY_ORGANIZATION_SLUG") + project_slug = current_app.config.get("SENTRY_PROJECT_SLUG") + if organization_slug and project_slug: + sentry_link = ( + f"https://sentry.io/{organization_slug}/{project_slug}/events/{id}" + ) # !!!NOTE!!!: do this after sentry stuff since calling logger.exception # seems to break the sentry sdk context where we no longer get back diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml index c559ba98..a97a3b11 100755 --- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml @@ -673,6 +673,53 @@ paths: schema: $ref: "#/components/schemas/Workflow" + /process-instances/{modified_process_model_identifier}/{process_instance_id}/task-info: + parameters: + - name: modified_process_model_identifier + in: path + required: true + description: The unique id of an existing process model + schema: + type: string + - name: process_instance_id + in: path + required: true + description: The unique id of an existing process instance. + schema: + type: integer + - name: process_identifier + in: query + required: false + description: The identifier of the process to use for the diagram. Useful for displaying the diagram for a call activity. + schema: + type: string + - name: all_tasks + in: query + required: false + description: If true, this wil return all tasks associated with the process instance and not just user tasks. + schema: + type: boolean + - name: spiff_step + in: query + required: false + description: If set will return the tasks as they were during a specific step of execution. + schema: + type: integer + get: + tags: + - Process Instances + operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_task_list_without_task_data + summary: returns the list of all user tasks associated with process instance without the task data + responses: + "200": + description: list of tasks + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Task" + /process-instances/{modified_process_model_identifier}/{process_instance_id}: parameters: - name: modified_process_model_identifier @@ -1132,8 +1179,8 @@ paths: get: tags: - Process Instances - operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_task_list - summary: returns the list of all user tasks associated with process instance + operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_task_list_with_task_data + summary: returns the list of all user tasks associated with process instance with the task data responses: "200": description: list of tasks diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml index 99790fed..14061da7 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml @@ -123,12 +123,12 @@ permissions: users: [] allowed_permissions: [read] uri: /v1.0/processes - - task-data-read: - groups: [demo] - users: [] - allowed_permissions: [read] - uri: /v1.0/task-data/* + # + # task-data-read: + # groups: [demo] + # users: [] + # allowed_permissions: [read] + # uri: /v1.0/task-data/* manage-procurement-admin: diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_logging.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_logging.py index b0b90887..532a6c09 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_logging.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_logging.py @@ -8,7 +8,7 @@ from flask_bpmn.models.db import SpiffworkflowBaseDBModel @dataclass class SpiffLoggingModel(SpiffworkflowBaseDBModel): - """LoggingModel.""" + """SpiffLoggingModel.""" __tablename__ = "spiff_logging" id: int = db.Column(db.Integer, primary_key=True) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index ba2f65c2..616d07ca 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -1440,11 +1440,44 @@ def get_tasks( return make_response(jsonify(response_json), 200) -def process_instance_task_list( +def process_instance_task_list_without_task_data( modified_process_model_identifier: str, process_instance_id: int, all_tasks: bool = False, spiff_step: int = 0, +) -> flask.wrappers.Response: + """Process_instance_task_list_without_task_data.""" + return process_instance_task_list( + modified_process_model_identifier, + process_instance_id, + all_tasks, + spiff_step, + get_task_data=False, + ) + + +def process_instance_task_list_with_task_data( + modified_process_model_identifier: str, + process_instance_id: int, + all_tasks: bool = False, + spiff_step: int = 0, +) -> flask.wrappers.Response: + """Process_instance_task_list_with_task_data.""" + return process_instance_task_list( + modified_process_model_identifier, + process_instance_id, + all_tasks, + spiff_step, + get_task_data=True, + ) + + +def process_instance_task_list( + _modified_process_model_identifier: str, + process_instance_id: int, + all_tasks: bool = False, + spiff_step: int = 0, + get_task_data: bool = False, ) -> flask.wrappers.Response: """Process_instance_task_list.""" process_instance = find_process_instance_by_id_or_raise(process_instance_id) @@ -1475,7 +1508,8 @@ def process_instance_task_list( tasks = [] for spiff_task in spiff_tasks: task = ProcessInstanceService.spiff_task_to_api_task(spiff_task) - task.data = spiff_task.data + if get_task_data: + task.data = spiff_task.data tasks.append(task) return make_response(jsonify(tasks), 200) diff --git a/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx b/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx index f84465c8..4ba04352 100644 --- a/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx +++ b/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx @@ -14,7 +14,8 @@ export const useUriListForPermissions = () => { processInstanceListPath: '/v1.0/process-instances', processInstanceLogListPath: `/v1.0/logs/${params.process_model_id}/${params.process_instance_id}`, processInstanceReportListPath: '/v1.0/process-instances/reports', - processInstanceTaskListPath: `/v1.0/task-data/${params.process_model_id}/${params.process_instance_id}`, + processInstanceTaskListPath: `/v1.0/process-instances/${params.process_model_id}/${params.process_instance_id}/task-info`, + processInstanceTaskListDataPath: `/v1.0/task-data/${params.process_model_id}/${params.process_instance_id}`, processModelCreatePath: `/v1.0/process-models/${params.process_group_id}`, processModelFileCreatePath: `/v1.0/process-models/${params.process_model_id}/files`, processModelFileShowPath: `/v1.0/process-models/${params.process_model_id}/files/${params.file_name}`, diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx index 7a9b9794..1adb585b 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx @@ -70,8 +70,10 @@ export default function ProcessInstanceShow() { const permissionRequestData: PermissionsToCheck = { [targetUris.messageInstanceListPath]: ['GET'], [targetUris.processInstanceTaskListPath]: ['GET'], + [targetUris.processInstanceTaskListDataPath]: ['GET', 'PUT'], [targetUris.processInstanceActionPath]: ['DELETE'], [targetUris.processInstanceLogListPath]: ['GET'], + [targetUris.processModelShowPath]: ['PUT'], [`${targetUris.processInstanceActionPath}/suspend`]: ['PUT'], [`${targetUris.processInstanceActionPath}/terminate`]: ['PUT'], [`${targetUris.processInstanceActionPath}/resume`]: ['PUT'], @@ -104,9 +106,15 @@ export default function ProcessInstanceShow() { if (typeof params.spiff_step !== 'undefined') { taskParams = `${taskParams}&spiff_step=${params.spiff_step}`; } - if (ability.can('GET', targetUris.processInstanceTaskListPath)) { + let taskPath = ''; + if (ability.can('GET', targetUris.processInstanceTaskListDataPath)) { + taskPath = `${targetUris.processInstanceTaskListDataPath}${taskParams}`; + } else if (ability.can('GET', targetUris.processInstanceTaskListPath)) { + taskPath = `${targetUris.processInstanceTaskListPath}${taskParams}`; + } + if (taskPath) { HttpService.makeCallToBackend({ - path: `${targetUris.processInstanceTaskListPath}${taskParams}`, + path: taskPath, successCallback: setTasks, failureCallback: processTaskFailure, }); @@ -442,7 +450,9 @@ export default function ProcessInstanceShow() { const canEditTaskData = (task: any) => { return ( - task.state === 'READY' && showingLastSpiffStep(processInstance as any) + ability.can('PUT', targetUris.processInstanceTaskListDataPath) && + task.state === 'READY' && + showingLastSpiffStep(processInstance as any) ); }; @@ -491,7 +501,10 @@ export default function ProcessInstanceShow() { const taskDataButtons = (task: any) => { const buttons = []; - if (task.type === 'Script Task') { + if ( + task.type === 'Script Task' && + ability.can('PUT', targetUris.processModelShowPath) + ) { buttons.push( + + {canViewXml && ( + + )} + ); } diff --git a/src/components/TasksForMyOpenProcesses.tsx b/src/components/TasksForMyOpenProcesses.tsx index deb2030e..297f2071 100644 --- a/src/components/TasksForMyOpenProcesses.tsx +++ b/src/components/TasksForMyOpenProcesses.tsx @@ -41,7 +41,7 @@ export default function MyOpenProcesses() { }); }; getTasks(); - refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks); + return refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks); }, [searchParams]); const buildTable = () => { diff --git a/src/components/TasksWaitingForMyGroups.tsx b/src/components/TasksWaitingForMyGroups.tsx index 565cd4a5..5b05dcd0 100644 --- a/src/components/TasksWaitingForMyGroups.tsx +++ b/src/components/TasksWaitingForMyGroups.tsx @@ -41,7 +41,7 @@ export default function TasksWaitingForMyGroups() { }); }; getTasks(); - refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks); + return refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks); }, [searchParams]); const buildTable = () => { diff --git a/src/helpers.tsx b/src/helpers.tsx index 6781ada9..8f625533 100644 --- a/src/helpers.tsx +++ b/src/helpers.tsx @@ -208,5 +208,29 @@ export const refreshAtInterval = ( () => clearInterval(intervalRef), timeout * 1000 ); - return [intervalRef, timeoutRef]; + return () => { + clearInterval(intervalRef); + clearTimeout(timeoutRef); + }; +}; + +const getChildProcesses = (bpmnElement: any) => { + let elements: string[] = []; + bpmnElement.children.forEach((c: any) => { + if (c.type === 'bpmn:Participant') { + if (c.businessObject.processRef) { + elements.push(c.businessObject.processRef.id); + } + elements = [...elements, ...getChildProcesses(c)]; + } else if (c.type === 'bpmn:SubProcess') { + elements.push(c.id); + } + }); + return elements; +}; + +export const getBpmnProcessIdentifiers = (rootBpmnElement: any) => { + const childProcesses = getChildProcesses(rootBpmnElement); + childProcesses.push(rootBpmnElement.businessObject.id); + return childProcesses; }; diff --git a/src/hooks/UriListForPermissions.tsx b/src/hooks/UriListForPermissions.tsx index f84465c8..4ba04352 100644 --- a/src/hooks/UriListForPermissions.tsx +++ b/src/hooks/UriListForPermissions.tsx @@ -14,7 +14,8 @@ export const useUriListForPermissions = () => { processInstanceListPath: '/v1.0/process-instances', processInstanceLogListPath: `/v1.0/logs/${params.process_model_id}/${params.process_instance_id}`, processInstanceReportListPath: '/v1.0/process-instances/reports', - processInstanceTaskListPath: `/v1.0/task-data/${params.process_model_id}/${params.process_instance_id}`, + processInstanceTaskListPath: `/v1.0/process-instances/${params.process_model_id}/${params.process_instance_id}/task-info`, + processInstanceTaskListDataPath: `/v1.0/task-data/${params.process_model_id}/${params.process_instance_id}`, processModelCreatePath: `/v1.0/process-models/${params.process_group_id}`, processModelFileCreatePath: `/v1.0/process-models/${params.process_model_id}/files`, processModelFileShowPath: `/v1.0/process-models/${params.process_model_id}/files/${params.file_name}`, diff --git a/src/interfaces.ts b/src/interfaces.ts index 079e4cdc..6afb1144 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -11,6 +11,13 @@ export interface RecentProcessModel { processModelDisplayName: string; } +export interface ProcessInstanceTask { + id: string; + state: string; + process_identifier: string; + name: string; +} + export interface ProcessReference { name: string; // The process or decision Display name. identifier: string; // The unique id of the process @@ -39,6 +46,7 @@ export interface ProcessInstance { id: number; process_model_identifier: string; process_model_display_name: string; + spiff_step?: number; } export interface MessageCorrelationProperties { diff --git a/src/routes/ProcessInstanceList.tsx b/src/routes/ProcessInstanceList.tsx index b6c08b21..1d75db56 100644 --- a/src/routes/ProcessInstanceList.tsx +++ b/src/routes/ProcessInstanceList.tsx @@ -21,10 +21,11 @@ export default function ProcessInstanceList() { diff --git a/src/routes/ProcessInstanceShow.tsx b/src/routes/ProcessInstanceShow.tsx index 9a0495d1..1adb585b 100644 --- a/src/routes/ProcessInstanceShow.tsx +++ b/src/routes/ProcessInstanceShow.tsx @@ -1,6 +1,11 @@ import { useContext, useEffect, useState } from 'react'; import Editor from '@monaco-editor/react'; -import { useParams, useNavigate, Link } from 'react-router-dom'; +import { + useParams, + useNavigate, + Link, + useSearchParams, +} from 'react-router-dom'; import { TrashCan, StopOutline, @@ -34,15 +39,21 @@ import { import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; import ErrorContext from '../contexts/ErrorContext'; import { useUriListForPermissions } from '../hooks/UriListForPermissions'; -import { PermissionsToCheck } from '../interfaces'; +import { + PermissionsToCheck, + ProcessInstance, + ProcessInstanceTask, +} from '../interfaces'; import { usePermissionFetcher } from '../hooks/PermissionService'; export default function ProcessInstanceShow() { const navigate = useNavigate(); const params = useParams(); + const [searchParams] = useSearchParams(); - const [processInstance, setProcessInstance] = useState(null); - const [tasks, setTasks] = useState | null>(null); + const [processInstance, setProcessInstance] = + useState(null); + const [tasks, setTasks] = useState(null); const [tasksCallHadError, setTasksCallHadError] = useState(false); const [taskToDisplay, setTaskToDisplay] = useState(null); const [taskDataToDisplay, setTaskDataToDisplay] = useState(''); @@ -59,8 +70,10 @@ export default function ProcessInstanceShow() { const permissionRequestData: PermissionsToCheck = { [targetUris.messageInstanceListPath]: ['GET'], [targetUris.processInstanceTaskListPath]: ['GET'], + [targetUris.processInstanceTaskListDataPath]: ['GET', 'PUT'], [targetUris.processInstanceActionPath]: ['DELETE'], [targetUris.processInstanceLogListPath]: ['GET'], + [targetUris.processModelShowPath]: ['PUT'], [`${targetUris.processInstanceActionPath}/suspend`]: ['PUT'], [`${targetUris.processInstanceActionPath}/terminate`]: ['PUT'], [`${targetUris.processInstanceActionPath}/resume`]: ['PUT'], @@ -80,17 +93,28 @@ export default function ProcessInstanceShow() { const processTaskFailure = () => { setTasksCallHadError(true); }; + let queryParams = ''; + const processIdentifier = searchParams.get('process_identifier'); + if (processIdentifier) { + queryParams = `?process_identifier=${processIdentifier}`; + } HttpService.makeCallToBackend({ - path: `/process-instances/${modifiedProcessModelId}/${params.process_instance_id}`, + path: `/process-instances/${modifiedProcessModelId}/${params.process_instance_id}${queryParams}`, successCallback: setProcessInstance, }); let taskParams = '?all_tasks=true'; if (typeof params.spiff_step !== 'undefined') { taskParams = `${taskParams}&spiff_step=${params.spiff_step}`; } - if (ability.can('GET', targetUris.processInstanceTaskListPath)) { + let taskPath = ''; + if (ability.can('GET', targetUris.processInstanceTaskListDataPath)) { + taskPath = `${targetUris.processInstanceTaskListDataPath}${taskParams}`; + } else if (ability.can('GET', targetUris.processInstanceTaskListPath)) { + taskPath = `${targetUris.processInstanceTaskListPath}${taskParams}`; + } + if (taskPath) { HttpService.makeCallToBackend({ - path: `${targetUris.processInstanceTaskListPath}${taskParams}`, + path: taskPath, successCallback: setTasks, failureCallback: processTaskFailure, }); @@ -98,7 +122,14 @@ export default function ProcessInstanceShow() { setTasksCallHadError(true); } } - }, [params, modifiedProcessModelId, permissionsLoaded, ability, targetUris]); + }, [ + params, + modifiedProcessModelId, + permissionsLoaded, + ability, + targetUris, + searchParams, + ]); const deleteProcessInstance = () => { HttpService.makeCallToBackend({ @@ -140,12 +171,12 @@ export default function ProcessInstanceShow() { const getTaskIds = () => { const taskIds = { completed: [], readyOrWaiting: [] }; if (tasks) { - tasks.forEach(function getUserTasksElement(task: any) { + tasks.forEach(function getUserTasksElement(task: ProcessInstanceTask) { if (task.state === 'COMPLETED') { - (taskIds.completed as any).push(task.name); + (taskIds.completed as any).push(task); } if (task.state === 'READY' || task.state === 'WAITING') { - (taskIds.readyOrWaiting as any).push(task.name); + (taskIds.readyOrWaiting as any).push(task); } }); } @@ -175,15 +206,18 @@ export default function ProcessInstanceShow() { label: any, distance: number ) => { + const processIdentifier = searchParams.get('process_identifier'); + let queryParams = ''; + if (processIdentifier) { + queryParams = `?process_identifier=${processIdentifier}`; + } return ( {label} @@ -364,10 +398,15 @@ export default function ProcessInstanceShow() { } }; - const handleClickedDiagramTask = (shapeElement: any) => { + const handleClickedDiagramTask = ( + shapeElement: any, + bpmnProcessIdentifiers: any + ) => { if (tasks) { const matchingTask: any = tasks.find( - (task: any) => task.name === shapeElement.id + (task: any) => + task.name === shapeElement.id && + bpmnProcessIdentifiers.includes(task.process_identifier) ); if (matchingTask) { setTaskToDisplay(matchingTask); @@ -411,7 +450,9 @@ export default function ProcessInstanceShow() { const canEditTaskData = (task: any) => { return ( - task.state === 'READY' && showingLastSpiffStep(processInstance as any) + ability.can('PUT', targetUris.processInstanceTaskListDataPath) && + task.state === 'READY' && + showingLastSpiffStep(processInstance as any) ); }; @@ -460,7 +501,10 @@ export default function ProcessInstanceShow() { const taskDataButtons = (task: any) => { const buttons = []; - if (task.type === 'Script Task') { + if ( + task.type === 'Script Task' && + ability.can('PUT', targetUris.processModelShowPath) + ) { buttons.push( ); buttons.push( + ) : null} { setTask(result); - if (ability.can('GET', targetUris.processInstanceTaskListPath)) { + if (ability.can('GET', targetUris.processInstanceTaskListDataPath)) { HttpService.makeCallToBackend({ path: `/task-data/${modifyProcessIdentifierForPathParam( result.process_model_identifier diff --git a/src/services/HttpService.ts b/src/services/HttpService.ts index 119765a7..78a29d07 100644 --- a/src/services/HttpService.ts +++ b/src/services/HttpService.ts @@ -26,7 +26,7 @@ type backendCallProps = { postBody?: any; }; -class UnauthenticatedError extends Error { +export class UnauthenticatedError extends Error { constructor(message: string) { super(message); this.name = 'UnauthenticatedError'; diff --git a/src/services/UserService.ts b/src/services/UserService.ts index 84e84d6f..df0f213e 100644 --- a/src/services/UserService.ts +++ b/src/services/UserService.ts @@ -27,8 +27,8 @@ const doLogout = () => { const idToken = getIdToken(); localStorage.removeItem('jwtAccessToken'); localStorage.removeItem('jwtIdToken'); - const redirctUrl = `${window.location.origin}/`; - const url = `${BACKEND_BASE_URL}/logout?redirect_url=${redirctUrl}&id_token=${idToken}`; + const redirectUrl = `${window.location.origin}`; + const url = `${BACKEND_BASE_URL}/logout?redirect_url=${redirectUrl}&id_token=${idToken}`; window.location.href = url; }; From 517ba99be74563f3a60092fff3c86a8762e28cba Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 16 Dec 2022 13:23:59 -0500 Subject: [PATCH 13/20] Squashed 'flask-bpmn/' changes from 53051e39..0f2d249d 0f2d249d added new api endpoint to get task-info so users with access to process instances can see the tasks but not the data git-subtree-dir: flask-bpmn git-subtree-split: 0f2d249d0e799bec912d46132e9ef9754fdacbd7 --- src/flask_bpmn/api/api_error.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/flask_bpmn/api/api_error.py b/src/flask_bpmn/api/api_error.py index eb390abe..c782c2d3 100644 --- a/src/flask_bpmn/api/api_error.py +++ b/src/flask_bpmn/api/api_error.py @@ -170,15 +170,17 @@ def set_user_sentry_context() -> None: def handle_exception(exception: Exception) -> flask.wrappers.Response: """Handles unexpected exceptions.""" set_user_sentry_context() - id = capture_exception(exception) - organization_slug = current_app.config.get("SENTRY_ORGANIZATION_SLUG") - project_slug = current_app.config.get("SENTRY_PROJECT_SLUG") sentry_link = None - if organization_slug and project_slug: - sentry_link = ( - f"https://sentry.io/{organization_slug}/{project_slug}/events/{id}" - ) + if not isinstance(exception, ApiError) or exception.error_code != "invalid_token": + id = capture_exception(exception) + + organization_slug = current_app.config.get("SENTRY_ORGANIZATION_SLUG") + project_slug = current_app.config.get("SENTRY_PROJECT_SLUG") + if organization_slug and project_slug: + sentry_link = ( + f"https://sentry.io/{organization_slug}/{project_slug}/events/{id}" + ) # !!!NOTE!!!: do this after sentry stuff since calling logger.exception # seems to break the sentry sdk context where we no longer get back From 3a9f86c25bd1843521a7398feb68f3c5ffdf0ba6 Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 16 Dec 2022 13:24:52 -0500 Subject: [PATCH 14/20] updated flask-bpmn so it does not send invalid token exceptions to sentry w/ burnettk --- spiffworkflow-backend/poetry.lock | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/spiffworkflow-backend/poetry.lock b/spiffworkflow-backend/poetry.lock index a23004b4..5bcb2d0f 100644 --- a/spiffworkflow-backend/poetry.lock +++ b/spiffworkflow-backend/poetry.lock @@ -654,7 +654,7 @@ werkzeug = "*" type = "git" url = "https://github.com/sartography/flask-bpmn" reference = "main" -resolved_reference = "860f2387bebdaa9220e9fbf6f8fa7f74e805d0d4" +resolved_reference = "0f2d249d0e799bec912d46132e9ef9754fdacbd7" [[package]] name = "Flask-Cors" @@ -1851,7 +1851,7 @@ lxml = "*" type = "git" url = "https://github.com/sartography/SpiffWorkflow" reference = "main" -resolved_reference = "ffb1686757f944065580dd2db8def73d6c1f0134" +resolved_reference = "841bd63017bb1d92858456393f144b4e5b23c994" [[package]] name = "SQLAlchemy" @@ -2563,7 +2563,6 @@ greenlet = [ {file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5b0ff9878333823226d270417f24f4d06f235cb3e54d1103b71ea537a6a86ce"}, {file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be9e0fb2ada7e5124f5282d6381903183ecc73ea019568d6d63d33f25b2a9000"}, {file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b493db84d124805865adc587532ebad30efa68f79ad68f11b336e0a51ec86c2"}, - {file = "greenlet-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0459d94f73265744fee4c2d5ec44c6f34aa8a31017e6e9de770f7bcf29710be9"}, {file = "greenlet-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a20d33124935d27b80e6fdacbd34205732660e0a1d35d8b10b3328179a2b51a1"}, {file = "greenlet-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:ea688d11707d30e212e0110a1aac7f7f3f542a259235d396f88be68b649e47d1"}, {file = "greenlet-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:afe07421c969e259e9403c3bb658968702bc3b78ec0b6fde3ae1e73440529c23"}, @@ -2572,7 +2571,6 @@ greenlet = [ {file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:659f167f419a4609bc0516fb18ea69ed39dbb25594934bd2dd4d0401660e8a1e"}, {file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:356e4519d4dfa766d50ecc498544b44c0249b6de66426041d7f8b751de4d6b48"}, {file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:811e1d37d60b47cb8126e0a929b58c046251f28117cb16fcd371eed61f66b764"}, - {file = "greenlet-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d38ffd0e81ba8ef347d2be0772e899c289b59ff150ebbbbe05dc61b1246eb4e0"}, {file = "greenlet-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0109af1138afbfb8ae647e31a2b1ab030f58b21dd8528c27beaeb0093b7938a9"}, {file = "greenlet-2.0.1-cp38-cp38-win32.whl", hash = "sha256:88c8d517e78acdf7df8a2134a3c4b964415b575d2840a2746ddb1cc6175f8608"}, {file = "greenlet-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:d6ee1aa7ab36475035eb48c01efae87d37936a8173fc4d7b10bb02c2d75dd8f6"}, @@ -2581,7 +2579,6 @@ greenlet = [ {file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:505138d4fa69462447a562a7c2ef723c6025ba12ac04478bc1ce2fcc279a2db5"}, {file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cce1e90dd302f45716a7715517c6aa0468af0bf38e814ad4eab58e88fc09f7f7"}, {file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e9744c657d896c7b580455e739899e492a4a452e2dd4d2b3e459f6b244a638d"}, - {file = "greenlet-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:662e8f7cad915ba75d8017b3e601afc01ef20deeeabf281bd00369de196d7726"}, {file = "greenlet-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:41b825d65f31e394b523c84db84f9383a2f7eefc13d987f308f4663794d2687e"}, {file = "greenlet-2.0.1-cp39-cp39-win32.whl", hash = "sha256:db38f80540083ea33bdab614a9d28bcec4b54daa5aff1668d7827a9fc769ae0a"}, {file = "greenlet-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b23d2a46d53210b498e5b701a1913697671988f4bf8e10f935433f6e7c332fb6"}, @@ -2880,7 +2877,10 @@ orjson = [ {file = "orjson-3.8.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b68a42a31f8429728183c21fb440c21de1b62e5378d0d73f280e2d894ef8942e"}, {file = "orjson-3.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ff13410ddbdda5d4197a4a4c09969cb78c722a67550f0a63c02c07aadc624833"}, {file = "orjson-3.8.0-cp310-none-win_amd64.whl", hash = "sha256:2d81e6e56bbea44be0222fb53f7b255b4e7426290516771592738ca01dbd053b"}, + {file = "orjson-3.8.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:200eae21c33f1f8b02a11f5d88d76950cd6fd986d88f1afe497a8ae2627c49aa"}, + {file = "orjson-3.8.0-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:9529990f3eab54b976d327360aa1ff244a4b12cb5e4c5b3712fcdd96e8fe56d4"}, {file = "orjson-3.8.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e2defd9527651ad39ec20ae03c812adf47ef7662bdd6bc07dabb10888d70dc62"}, + {file = "orjson-3.8.0-cp311-none-win_amd64.whl", hash = "sha256:b21c7af0ff6228ca7105f54f0800636eb49201133e15ddb80ac20c1ce973ef07"}, {file = "orjson-3.8.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:9e6ac22cec72d5b39035b566e4b86c74b84866f12b5b0b6541506a080fb67d6d"}, {file = "orjson-3.8.0-cp37-cp37m-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:e2f4a5542f50e3d336a18cb224fc757245ca66b1fd0b70b5dd4471b8ff5f2b0e"}, {file = "orjson-3.8.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1418feeb8b698b9224b1f024555895169d481604d5d884498c1838d7412794c"}, @@ -2989,18 +2989,7 @@ psycopg2 = [ {file = "psycopg2-2.9.4.tar.gz", hash = "sha256:d529926254e093a1b669f692a3aa50069bc71faf5b0ecd91686a78f62767d52f"}, ] pyasn1 = [ - {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, - {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, - {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, - {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, - {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, - {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, - {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, - {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, - {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, - {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, - {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, ] pycodestyle = [ From c18056135104d3e39ee9be3112c7e028e99f7a8b Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 16 Dec 2022 14:21:04 -0500 Subject: [PATCH 15/20] updated some text for task tables w/ burnettk --- spiffworkflow-backend/src/spiffworkflow_backend/api.yml | 2 +- .../routes/process_api_blueprint.py | 6 +++--- .../src/components/{TasksTable.tsx => TaskListTable.tsx} | 9 ++++++--- .../src/components/TasksForMyOpenProcesses.tsx | 5 +++-- .../src/components/TasksWaitingForMe.tsx | 5 +++-- .../src/components/TasksWaitingForMyGroups.tsx | 9 +++++---- 6 files changed, 21 insertions(+), 15 deletions(-) rename spiffworkflow-frontend/src/components/{TasksTable.tsx => TaskListTable.tsx} (96%) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml index 3d78cb43..be9796aa 100755 --- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml @@ -1166,7 +1166,7 @@ paths: get: tags: - Process Instances - operationId: spiffworkflow_backend.routes.process_api_blueprint.user_groups_for_current_user + operationId: spiffworkflow_backend.routes.process_api_blueprint.user_group_list_for_current_user summary: Group identifiers for current logged in user responses: "200": diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index 7907ef97..294a6524 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -989,7 +989,7 @@ def process_instance_list( process_instance_query = process_instance_query.filter( SpiffLoggingModel.spiff_step == SpiffStepDetailsModel.spiff_step ) - if (group_identifier): + if group_identifier: process_instance_query = process_instance_query.join( GroupModel, GroupModel.identifier == group_identifier, @@ -1380,8 +1380,8 @@ def task_list_for_my_groups( ) -def user_groups_for_current_user() -> flask.wrappers.Response: - """User_groups_for_current_user.""" +def user_group_list_for_current_user() -> flask.wrappers.Response: + """User_group_list_for_current_user.""" groups = g.user.groups # TODO: filter out the default group and have a way to know what is the default group group_identifiers = [i.identifier for i in groups if i.identifier != "everybody"] diff --git a/spiffworkflow-frontend/src/components/TasksTable.tsx b/spiffworkflow-frontend/src/components/TaskListTable.tsx similarity index 96% rename from spiffworkflow-frontend/src/components/TasksTable.tsx rename to spiffworkflow-frontend/src/components/TaskListTable.tsx index 0c657016..d6688f22 100644 --- a/spiffworkflow-frontend/src/components/TasksTable.tsx +++ b/spiffworkflow-frontend/src/components/TaskListTable.tsx @@ -27,15 +27,17 @@ type OwnProps = { autoReload?: boolean; showStartedBy?: boolean; showWaitingOn?: boolean; + textToShowIfEmpty?: string; }; -export default function TasksTable({ +export default function TaskListTable({ apiPath, tableTitle, tableDescription, additionalParams, paginationQueryParamPrefix, paginationClassName, + textToShowIfEmpty, autoReload = false, showStartedBy = true, showWaitingOn = true, @@ -67,8 +69,9 @@ export default function TasksTable({ }; getTasks(); if (autoReload) { - refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks); + return refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks); } + return undefined; }, [ searchParams, additionalParams, @@ -164,7 +167,7 @@ export default function TasksTable({ if (pagination && pagination.total < 1) { return (

- Your groups have no task assignments at this time. + {textToShowIfEmpty}

); } diff --git a/spiffworkflow-frontend/src/components/TasksForMyOpenProcesses.tsx b/spiffworkflow-frontend/src/components/TasksForMyOpenProcesses.tsx index 7ee7edec..6634e8e8 100644 --- a/spiffworkflow-frontend/src/components/TasksForMyOpenProcesses.tsx +++ b/spiffworkflow-frontend/src/components/TasksForMyOpenProcesses.tsx @@ -1,15 +1,16 @@ -import TasksTable from './TasksTable'; +import TaskListTable from './TaskListTable'; const paginationQueryParamPrefix = 'tasks_for_my_open_processes'; export default function MyOpenProcesses() { return ( - diff --git a/spiffworkflow-frontend/src/components/TasksWaitingForMe.tsx b/spiffworkflow-frontend/src/components/TasksWaitingForMe.tsx index e253afd6..1939e4ba 100644 --- a/spiffworkflow-frontend/src/components/TasksWaitingForMe.tsx +++ b/spiffworkflow-frontend/src/components/TasksWaitingForMe.tsx @@ -1,13 +1,14 @@ -import TasksTable from './TasksTable'; +import TaskListTable from './TaskListTable'; export default function TasksWaitingForMe() { return ( - diff --git a/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx b/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx index 948aee6c..b7515c91 100644 --- a/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx +++ b/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; import HttpService from '../services/HttpService'; -import TasksTable from './TasksTable'; +import TaskListTable from './TaskListTable'; export default function TasksWaitingForMyGroups() { const [userGroups, setUserGroups] = useState(null); @@ -19,13 +19,14 @@ export default function TasksWaitingForMyGroups() { return userGroups.map((userGroup: string) => { return ( - From 2d37a0295f7ba078f4e0846ab0e2546621c61992 Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 16 Dec 2022 14:41:13 -0500 Subject: [PATCH 16/20] some more updates for text w/ burnettk --- .../src/components/TasksForMyOpenProcesses.tsx | 2 +- .../src/routes/CompletedInstances.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spiffworkflow-frontend/src/components/TasksForMyOpenProcesses.tsx b/spiffworkflow-frontend/src/components/TasksForMyOpenProcesses.tsx index 6634e8e8..be1d9042 100644 --- a/spiffworkflow-frontend/src/components/TasksForMyOpenProcesses.tsx +++ b/spiffworkflow-frontend/src/components/TasksForMyOpenProcesses.tsx @@ -7,7 +7,7 @@ export default function MyOpenProcesses() { { return ( <> -

Tasks completed by {userGroup} group

+

With tasks completed by group: {userGroup}

This is a list of instances with tasks that were completed by the{' '} {userGroup} group.

@@ -56,7 +56,7 @@ export default function CompletedInstances() { paginationClassName="with-large-bottom-margin" autoReload /> -

Tasks completed by me

+

With tasks completed by me

This is a list of instances where you have completed tasks.

@@ -66,7 +66,7 @@ export default function CompletedInstances() { perPageOptions={[2, 5, 25]} reportIdentifier="system_report_instances_with_tasks_completed_by_me" showReports={false} - textToShowIfEmpty="You have no completed tasks at this time." + textToShowIfEmpty="You have no completed instances at this time." paginationClassName="with-large-bottom-margin" /> {groupTableComponents()} From 3ed0a9c991a9da7136e7dae71f356d9826b40534 Mon Sep 17 00:00:00 2001 From: Kevin Burnett <18027+burnettk@users.noreply.github.com> Date: Fri, 16 Dec 2022 12:01:57 -0800 Subject: [PATCH 17/20] add a test group and put natalia in it --- .../permissions/terraform_deployed_environment.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml index 731de9ab..2adf3d9c 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml @@ -15,7 +15,6 @@ groups: jarrad, elizabeth, jon, - natalia, ] Finance Team: @@ -31,7 +30,6 @@ groups: jarrad, elizabeth, jon, - natalia, sasha, fin, fin1, @@ -56,6 +54,8 @@ groups: core, harmeet, ] + test: + users: [natalia] permissions: admin: @@ -165,3 +165,9 @@ permissions: users: [] allowed_permissions: [create, read] uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:* + + create-test-instances: + groups: ["test"] + users: [] + allowed_permissions: [create, read] + uri: /v1.0/process-instances/misc:test:* From d5f72cbd24b6d9fe118813cce57630f2c891ac91 Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 16 Dec 2022 15:12:14 -0500 Subject: [PATCH 18/20] fixed file upload of new files and get 1000 process models to list tiles page w/ burnettk --- .../src/components/ProcessModelListTiles.tsx | 2 +- spiffworkflow-frontend/src/routes/ProcessModelShow.tsx | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/spiffworkflow-frontend/src/components/ProcessModelListTiles.tsx b/spiffworkflow-frontend/src/components/ProcessModelListTiles.tsx index 1412635c..bf197823 100644 --- a/spiffworkflow-frontend/src/components/ProcessModelListTiles.tsx +++ b/spiffworkflow-frontend/src/components/ProcessModelListTiles.tsx @@ -35,7 +35,7 @@ export default function ProcessModelListTiles({ setProcessModels(result.results); }; // only allow 10 for now until we get the backend only returning certain models for user execution - let queryParams = '?per_page=20'; + let queryParams = '?per_page=1000'; if (processGroup) { queryParams = `${queryParams}&process_group_identifier=${processGroup.id}`; } else { diff --git a/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx b/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx index 15935b7d..fd9f7b85 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx @@ -418,12 +418,18 @@ export default function ProcessModelShow() { const checkDuplicateFile = (event: any) => { if (processModel && processModel.files.length > 0) { + let foundExistingFile = false; processModel.files.forEach((file) => { if (file.name === filesToUpload[0].name) { - displayOverwriteConfirmation(file.name); - setFileUploadEvent(event); + foundExistingFile = true; } }); + if (foundExistingFile) { + displayOverwriteConfirmation(filesToUpload[0].name); + setFileUploadEvent(event); + } else { + doFileUpload(event); + } } }; From 01472d98486a5f352775021a3da502fdd4b6591c Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 16 Dec 2022 15:31:05 -0500 Subject: [PATCH 19/20] fixed permissions w/ burnettk --- .../config/permissions/acceptance_tests.yml | 2 +- .../config/permissions/development.yml | 35 +++++-------------- .../config/permissions/staging.yml | 5 +++ .../terraform_deployed_environment.yml | 11 +++--- 4 files changed, 19 insertions(+), 34 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/acceptance_tests.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/acceptance_tests.yml index a10b5685..65ba240a 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/acceptance_tests.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/acceptance_tests.yml @@ -9,5 +9,5 @@ permissions: admin: groups: [admin, common-user] users: [] - allowed_permissions: [create, read, update, delete, list, instantiate] + allowed_permissions: [create, read, update, delete] uri: /* diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml index 248f2d93..10045b88 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml @@ -20,7 +20,6 @@ groups: jarrad, elizabeth, jon, - natalia, ] Finance Team: @@ -35,7 +34,6 @@ groups: jarrad, elizabeth, jon, - natalia, sasha, fin, fin1, @@ -68,6 +66,9 @@ groups: j, ] + test: + users: [natalia] + permissions: admin: groups: [admin] @@ -129,12 +130,6 @@ permissions: users: [] allowed_permissions: [read] uri: /v1.0/processes - # - # task-data-read: - # groups: [demo] - # users: [] - # allowed_permissions: [read] - # uri: /v1.0/task-data/* manage-procurement-admin: @@ -174,44 +169,30 @@ permissions: allowed_permissions: [create, read, update, delete] uri: /v1.0/process-groups/manage-procurement:procurement:* - manage-revenue-streams-instantiate: - groups: ["core-contributor", "demo"] - users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/* manage-revenue-streams-instances: groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create, read] uri: /v1.0/process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/* - manage-procurement-invoice-instantiate: - groups: ["core-contributor", "demo"] - users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:procurement:core-contributor-invoice-management:* manage-procurement-invoice-instances: groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create, read] uri: /v1.0/process-instances/manage-procurement:procurement:core-contributor-invoice-management:* - manage-procurement-instantiate: - groups: ["core-contributor", "demo"] - users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:vendor-lifecycle-management:* manage-procurement-instances: groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create, read] uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:* - core1-admin-models-instantiate: - groups: ["core-contributor", "Finance Team"] + create-test-instances: + groups: ["test"] users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/misc:category_number_one:process-model-with-form/process-instances + allowed_permissions: [create, read] + uri: /v1.0/process-instances/misc:test:* + core1-admin-instances: groups: ["core-contributor", "Finance Team"] users: [] diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/staging.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/staging.yml index 982b945c..1300f36b 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/staging.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/staging.yml @@ -80,6 +80,11 @@ permissions: users: [] allowed_permissions: [read] uri: /v1.0/service-tasks + user-groups-for-current-user: + groups: [everybody] + users: [] + allowed_permissions: [read] + uri: /v1.0/user-groups/for-current-user # read all for everybody diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml index 2adf3d9c..19ce385d 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml @@ -75,6 +75,11 @@ permissions: users: [] allowed_permissions: [read] uri: /v1.0/service-tasks + user-groups-for-current-user: + groups: [everybody] + users: [] + allowed_permissions: [read] + uri: /v1.0/user-groups/for-current-user # read all for everybody @@ -104,12 +109,6 @@ permissions: allowed_permissions: [read] uri: /v1.0/processes - task-data-read: - groups: [demo] - users: [] - allowed_permissions: [read] - uri: /v1.0/task-data/* - manage-procurement-admin: groups: ["Project Lead"] From 45fde3f2620a1593118aec0a6083d4853b733ea1 Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 16 Dec 2022 16:08:04 -0500 Subject: [PATCH 20/20] terminating a process instance is a POST w/ burnettk --- spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx index 1adb585b..88e5e3bb 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx @@ -74,9 +74,9 @@ export default function ProcessInstanceShow() { [targetUris.processInstanceActionPath]: ['DELETE'], [targetUris.processInstanceLogListPath]: ['GET'], [targetUris.processModelShowPath]: ['PUT'], - [`${targetUris.processInstanceActionPath}/suspend`]: ['PUT'], - [`${targetUris.processInstanceActionPath}/terminate`]: ['PUT'], - [`${targetUris.processInstanceActionPath}/resume`]: ['PUT'], + [`${targetUris.processInstanceActionPath}/suspend`]: ['POST'], + [`${targetUris.processInstanceActionPath}/terminate`]: ['POST'], + [`${targetUris.processInstanceActionPath}/resume`]: ['POST'], }; const { ability, permissionsLoaded } = usePermissionFetcher( permissionRequestData