diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml index 43e7685c6..49419ef95 100755 --- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml @@ -509,6 +509,119 @@ paths: schema: $ref: "#/components/schemas/OkTrue" + /process-instances/for-me: + parameters: + - name: process_model_identifier + in: query + required: false + description: The unique id of an existing process model. + schema: + type: string + - name: page + in: query + required: false + description: The page number to return. Defaults to page 1. + schema: + type: integer + - name: per_page + in: query + required: false + description: The page number to return. Defaults to page 1. + schema: + type: integer + - name: start_from + in: query + required: false + description: For filtering - beginning of start window - in seconds since epoch + schema: + type: integer + - name: start_to + in: query + required: false + description: For filtering - end of start window - in seconds since epoch + schema: + type: integer + - name: end_from + in: query + required: false + description: For filtering - beginning of end window - in seconds since epoch + schema: + type: integer + - name: end_to + in: query + required: false + description: For filtering - end of end window - in seconds since epoch + schema: + type: integer + - name: process_status + in: query + required: false + description: For filtering - not_started, user_input_required, waiting, complete, error, or suspended + schema: + type: string + - name: initiated_by_me + in: query + required: false + description: For filtering - show instances initiated by me + schema: + type: boolean + - name: with_tasks_completed_by_me + in: query + required: false + description: For filtering - show instances with tasks completed by me + schema: + type: boolean + - name: with_tasks_completed_by_my_group + in: query + required: false + description: For filtering - show instances with tasks completed by my group + schema: + type: boolean + - name: with_relation_to_me + in: query + required: false + description: For filtering - show instances that have something to do with me + schema: + type: boolean + - name: user_filter + in: query + required: false + description: For filtering - indicates the user has manually entered a query + schema: + type: boolean + - name: report_identifier + in: query + required: false + description: Specifies the identifier of a report to use, if any + schema: + type: string + - name: report_id + in: query + required: false + 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_for_me + summary: Returns a list of process instances that are associated with me. + tags: + - Process Instances + responses: + "200": + description: Workflow. + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Workflow" + /process-instances: parameters: - name: process_model_identifier @@ -609,7 +722,7 @@ paths: 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 + summary: Returns a list of process instances. tags: - Process Instances responses: @@ -732,6 +845,39 @@ paths: items: $ref: "#/components/schemas/Task" + /process-instances/for-me/{modified_process_model_identifier}/{process_instance_id}: + 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 + get: + tags: + - Process Instances + operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_show_for_me + summary: Show information about a process instance that is associated with me + responses: + "200": + description: One Process Instance + content: + application/json: + schema: + $ref: "#/components/schemas/Workflow" + /process-instances/{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 df2839f32..bbd4c46cd 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml @@ -115,11 +115,11 @@ permissions: users: [] allowed_permissions: [read] uri: /v1.0/process-models/* - read-all-process-instance: + read-all-process-instance-for-me: groups: [everybody] users: [] allowed_permissions: [read] - uri: /v1.0/process-instances/* + uri: /v1.0/process-instances/for-me/* read-process-instance-reports: groups: [everybody] users: [] 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 d1f761dbe..eda304e96 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -848,6 +848,37 @@ def message_start( ) +def process_instance_list_for_me( + process_model_identifier: Optional[str] = None, + page: int = 1, + per_page: int = 100, + start_from: Optional[int] = None, + start_to: Optional[int] = None, + end_from: Optional[int] = None, + end_to: Optional[int] = None, + process_status: Optional[str] = None, + user_filter: Optional[bool] = False, + report_identifier: Optional[str] = None, + report_id: Optional[int] = None, + group_identifier: Optional[str] = None, +) -> flask.wrappers.Response: + return process_instance_list( + process_model_identifier=process_model_identifier, + page=page, + per_page=per_page, + start_from=start_from, + start_to=start_to, + end_from=end_from, + end_to=end_to, + process_status=process_status, + user_filter=user_filter, + report_identifier=report_identifier, + report_id=report_id, + group_identifier=group_identifier, + with_relation_to_me=True, + ) + + def process_instance_list( process_model_identifier: Optional[str] = None, page: int = 1, @@ -951,7 +982,6 @@ def process_instance_list( ProcessInstanceModel.status.in_(report_filter.process_status) # type: ignore ) - print(f"report_filter.with_relation_to_me: {report_filter.with_relation_to_me}") if report_filter.with_relation_to_me is True: process_instance_query = process_instance_query.outerjoin( ActiveTaskModel @@ -1143,14 +1173,53 @@ def process_instance_report_column_list() -> flask.wrappers.Response: return make_response(jsonify(table_columns + columns_for_metadata_strings), 200) +def process_instance_show_for_me( + modified_process_model_identifier: str, + process_instance_id: int, + process_identifier: Optional[str] = None, +) -> flask.wrappers.Response: + process_instance = ProcessInstanceModel.query.filter_by(id=process_instance_id).outerjoin( + ActiveTaskModel + ).outerjoin( + ActiveTaskUserModel, + and_( + ActiveTaskModel.id == ActiveTaskUserModel.active_task_id, + ActiveTaskUserModel.user_id == g.user.id, + ), + ).filter( + or_( + ActiveTaskUserModel.id.is_not(None), + ProcessInstanceModel.process_initiator_id == g.user.id, + ) + ).first() + + if process_instance is None: + raise ( + ApiError( + error_code="process_instance_cannot_be_found", + message=f"Process instance with id {process_instance_id} cannot be found that is associated with you.", + status_code=400, + ) + ) + + return _get_process_instance(process_instance=process_instance, modified_process_model_identifier=modified_process_model_identifier, process_identifier=process_identifier) + + def process_instance_show( modified_process_model_identifier: str, process_instance_id: int, process_identifier: Optional[str] = None, ) -> flask.wrappers.Response: """Create_process_instance.""" - process_model_identifier = modified_process_model_identifier.replace(":", "/") process_instance = find_process_instance_by_id_or_raise(process_instance_id) + return _get_process_instance(process_instance=process_instance, modified_process_model_identifier=modified_process_model_identifier, process_identifier=process_identifier) + +def _get_process_instance( + modified_process_model_identifier: str, + process_instance: ProcessInstanceModel, + process_identifier: Optional[str] = None, +) -> flask.wrappers.Response: + process_model_identifier = modified_process_model_identifier.replace(":", "/") current_version_control_revision = GitService.get_current_revision() process_model_with_diagram = None diff --git a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx index 06f7793c4..92cec2050 100644 --- a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx +++ b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx @@ -70,6 +70,7 @@ const REFRESH_INTERVAL = 5; const REFRESH_TIMEOUT = 600; type OwnProps = { + apiPath?: string; filtersEnabled?: boolean; processModelFullIdentifier?: string; paginationQueryParamPrefix?: string; @@ -80,6 +81,7 @@ type OwnProps = { paginationClassName?: string; autoReload?: boolean; additionalParams?: string; + variant?: string; }; interface dateParameters { @@ -87,6 +89,7 @@ interface dateParameters { } export default function ProcessInstanceListTable({ + apiPath = '/process-instances', filtersEnabled = true, processModelFullIdentifier, paginationQueryParamPrefix, @@ -97,6 +100,7 @@ export default function ProcessInstanceListTable({ textToShowIfEmpty, paginationClassName, autoReload = false, + variant = 'for-me', }: OwnProps) { const params = useParams(); const [searchParams] = useSearchParams(); @@ -126,6 +130,11 @@ export default function ProcessInstanceListTable({ const setErrorMessage = (useContext as any)(ErrorContext)[1]; + const processInstancePathPrefix = + variant === 'all' + ? '/admin/process-instances' + : '/admin/process-instances/for-me'; + const [processStatusAllOptions, setProcessStatusAllOptions] = useState( [] ); @@ -260,7 +269,7 @@ export default function ProcessInstanceListTable({ } HttpService.makeCallToBackend({ - path: `/process-instances?${queryParamString}`, + path: `${apiPath}?${queryParamString}`, successCallback: setProcessInstancesFromResult, }); } @@ -516,7 +525,7 @@ export default function ProcessInstanceListTable({ setErrorMessage(null); setProcessInstanceReportJustSaved(null); - navigate(`/admin/process-instances?${queryParamString}`); + navigate(`${processInstancePathPrefix}?${queryParamString}`); }; const dateComponent = ( @@ -615,7 +624,7 @@ export default function ProcessInstanceListTable({ setErrorMessage(null); setProcessInstanceReportJustSaved(mode || null); - navigate(`/admin/process-instances${queryParamString}`); + navigate(`${processInstancePathPrefix}${queryParamString}`); }; const reportColumns = () => { @@ -1081,7 +1090,7 @@ export default function ProcessInstanceListTable({ return ( {id} diff --git a/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx b/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx index a9895c71c..46be60d54 100644 --- a/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx +++ b/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx @@ -9,16 +9,16 @@ export const useUriListForPermissions = () => { messageInstanceListPath: '/v1.0/messages', processGroupListPath: '/v1.0/process-groups', processGroupShowPath: `/v1.0/process-groups/${params.process_group_id}`, - processInstanceCreatePath: `/v1.0/process-instances/${params.process_model_id}`, processInstanceActionPath: `/v1.0/process-instances/${params.process_model_id}/${params.process_instance_id}`, - processInstanceResumePath: `/v1.0/process-instance-resume/${params.process_model_id}/${params.process_instance_id}`, - processInstanceSuspendPath: `/v1.0/process-instance-suspend/${params.process_model_id}/${params.process_instance_id}`, - processInstanceTerminatePath: `/v1.0/process-instance-terminate/${params.process_model_id}/${params.process_instance_id}`, + processInstanceCreatePath: `/v1.0/process-instances/${params.process_model_id}`, 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/process-instances/${params.process_model_id}/${params.process_instance_id}/task-info`, + processInstanceResumePath: `/v1.0/process-instance-resume/${params.process_model_id}/${params.process_instance_id}`, + processInstanceSuspendPath: `/v1.0/process-instance-suspend/${params.process_model_id}/${params.process_instance_id}`, processInstanceTaskListDataPath: `/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`, + processInstanceTerminatePath: `/v1.0/process-instance-terminate/${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/AdminRoutes.tsx b/spiffworkflow-frontend/src/routes/AdminRoutes.tsx index da6cae356..d24c2b6e2 100644 --- a/spiffworkflow-frontend/src/routes/AdminRoutes.tsx +++ b/spiffworkflow-frontend/src/routes/AdminRoutes.tsx @@ -62,21 +62,25 @@ export default function AdminRoutes() { path="process-models/:process_model_id/files/:file_name" element={} /> - } - /> } /> + } + /> + } + /> } + element={} /> } + element={} /> } /> - } /> + } + /> + } + /> + } + /> } /> } /> { const processModelFullIdentifier = getProcessModelFullIdentifierFromSearchParams(searchParams); @@ -33,13 +51,46 @@ export default function ProcessInstanceList() { }; const processInstanceTitleElement = () => { - return

Process Instances

; + if (variant === 'all') { + return

All Process Instances

; + } + return

My Process Instances

; }; + + let apiPath = '/process-instances/for-me'; + let selectedTabIndex = 0; + if (variant === 'all') { + apiPath = '/process-instances'; + selectedTabIndex = 1; + } return ( <> + + + { + navigate('/admin/process-instances/for-me'); + }} + > + For Me + + + { + navigate('/admin/process-instances/all'); + }} + > + All + + + + +
{processInstanceBreadcrumbElement()} {processInstanceTitleElement()} - + ); } diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx index db89bee87..37b515482 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx @@ -47,7 +47,11 @@ import { import { usePermissionFetcher } from '../hooks/PermissionService'; import ProcessInstanceClass from '../classes/ProcessInstanceClass'; -export default function ProcessInstanceShow() { +type OwnProps = { + variant: string; +}; + +export default function ProcessInstanceShow({ variant }: OwnProps) { const navigate = useNavigate(); const params = useParams(); const [searchParams] = useSearchParams(); @@ -99,8 +103,12 @@ export default function ProcessInstanceShow() { if (processIdentifier) { queryParams = `?process_identifier=${processIdentifier}`; } + let apiPath = '/process-instances/for-me'; + if (variant === 'all') { + apiPath = '/process-instances'; + } HttpService.makeCallToBackend({ - path: `/process-instances/${modifiedProcessModelId}/${params.process_instance_id}${queryParams}`, + path: `${apiPath}/${modifiedProcessModelId}/${params.process_instance_id}${queryParams}`, successCallback: setProcessInstance, }); let taskParams = '?all_tasks=true';