basic support to find a process instance by id w/ burnettk

This commit is contained in:
jasquat 2023-01-05 14:59:59 -05:00
parent 49a71d8981
commit 158cbb4bfd
8 changed files with 201 additions and 79 deletions

View File

@ -946,6 +946,27 @@ paths:
schema:
$ref: "#/components/schemas/Workflow"
/process-instances/find-by-id/{process_instance_id}:
parameters:
- name: process_instance_id
in: path
required: true
description: The unique id of an existing process instance.
schema:
type: integer
get:
operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_find_by_id
summary: Find a process instance based on its id only
tags:
- Process Instances
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

View File

@ -30,6 +30,12 @@ permissions:
allowed_permissions: [read]
uri: /*
process-instances-find-by-id:
groups: [everybody]
users: []
allowed_permissions: [read]
uri: /process-instances/find-by-id/*
tasks-crud:
groups: [everybody]
users: []

View File

@ -58,6 +58,14 @@ class ProcessModelInfo:
"""Id_for_file_path."""
return self.id.replace("/", os.sep)
@classmethod
def modify_process_identifier_for_path_param(cls, identifier: str) -> str:
"""Identifier."""
if "\\" in identifier:
raise Exception(f"Found backslash in identifier: {identifier}")
return identifier.replace("/", ":")
class ProcessModelInfoSchema(Schema):
"""ProcessModelInfoSchema."""

View File

@ -1,5 +1,7 @@
"""APIs for dealing with process groups, process models, and process instances."""
import json
from spiffworkflow_backend.services.authorization_service import AuthorizationService
from spiffworkflow_backend.models.process_model import ProcessModelInfo
from typing import Any
from typing import Dict
from typing import Optional
@ -88,9 +90,7 @@ def process_instance_run(
do_engine_steps: bool = True,
) -> flask.wrappers.Response:
"""Process_instance_run."""
process_instance = ProcessInstanceService().get_process_instance(
process_instance_id
)
process_instance = _find_process_instance_by_id_or_raise(process_instance_id)
if process_instance.status != "not_started":
raise ApiError(
error_code="process_instance_not_runnable",
@ -138,9 +138,7 @@ def process_instance_terminate(
modified_process_model_identifier: str,
) -> flask.wrappers.Response:
"""Process_instance_run."""
process_instance = ProcessInstanceService().get_process_instance(
process_instance_id
)
process_instance = _find_process_instance_by_id_or_raise(process_instance_id)
processor = ProcessInstanceProcessor(process_instance)
processor.terminate()
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
@ -151,9 +149,7 @@ def process_instance_suspend(
modified_process_model_identifier: str,
) -> flask.wrappers.Response:
"""Process_instance_suspend."""
process_instance = ProcessInstanceService().get_process_instance(
process_instance_id
)
process_instance = _find_process_instance_by_id_or_raise(process_instance_id)
processor = ProcessInstanceProcessor(process_instance)
processor.suspend()
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
@ -164,9 +160,7 @@ def process_instance_resume(
modified_process_model_identifier: str,
) -> flask.wrappers.Response:
"""Process_instance_resume."""
process_instance = ProcessInstanceService().get_process_instance(
process_instance_id
)
process_instance = _find_process_instance_by_id_or_raise(process_instance_id)
processor = ProcessInstanceProcessor(process_instance)
processor.resume()
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
@ -575,14 +569,38 @@ def process_instance_reset(
spiff_step: int = 0,
) -> flask.wrappers.Response:
"""Reset a process instance to a particular step."""
process_instance = ProcessInstanceService().get_process_instance(
process_instance_id
)
process_instance = _find_process_instance_by_id_or_raise(process_instance_id)
processor = ProcessInstanceProcessor(process_instance)
processor.reset_process(spiff_step)
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
def process_instance_find_by_id(
process_instance_id: int,
) -> flask.wrappers.Response:
process_instance = _find_process_instance_by_id_or_raise(process_instance_id)
modified_process_model_identifier = ProcessModelInfo.modify_process_identifier_for_path_param(process_instance.process_model_identifier)
process_instance_uri = f'/process-instances/{modified_process_model_identifier}/{process_instance.id}'
has_permission = AuthorizationService.user_has_permission(
user=g.user,
permission='read',
target_uri=process_instance_uri,
)
uri_type = None
if not has_permission:
process_instance = _find_process_instance_for_me_or_raise(process_instance_id)
uri_type = 'for-me'
response_json = {
"process_instance": process_instance,
"uri_type": uri_type,
}
return make_response(jsonify(response_json), 200)
def _get_process_instance(
modified_process_model_identifier: str,
process_instance: ProcessInstanceModel,

View File

@ -624,6 +624,83 @@ class AuthorizationService:
return permissions_to_assign
@classmethod
def set_basic_permissions(cls) -> list[PermissionToAssign]:
permissions_to_assign: list[PermissionToAssign] = []
permissions_to_assign.append(
PermissionToAssign(
permission="read", target_uri="/process-instances/for-me"
)
)
permissions_to_assign.append(
PermissionToAssign(permission="read", target_uri="/processes")
)
permissions_to_assign.append(
PermissionToAssign(permission="read", target_uri="/service-tasks")
)
permissions_to_assign.append(
PermissionToAssign(
permission="read", target_uri="/user-groups/for-current-user"
)
)
permissions_to_assign.append(
PermissionToAssign(
permission="read", target_uri="/process-instances/find-by-id/*"
)
)
for permission in ["create", "read", "update", "delete"]:
permissions_to_assign.append(
PermissionToAssign(
permission=permission, target_uri="/process-instances/reports/*"
)
)
permissions_to_assign.append(
PermissionToAssign(permission=permission, target_uri="/tasks/*")
)
return permissions_to_assign
@classmethod
def set_process_group_permissions(cls, target: str, permission_set: str) -> list[PermissionToAssign]:
permissions_to_assign: list[PermissionToAssign] = []
process_group_identifier = (
target.removeprefix("PG:").replace("/", ":").removeprefix(":")
)
process_related_path_segment = f"{process_group_identifier}:*"
if process_group_identifier == "ALL":
process_related_path_segment = "*"
target_uris = [
f"/process-groups/{process_related_path_segment}",
f"/process-models/{process_related_path_segment}",
]
permissions_to_assign = (
permissions_to_assign
+ cls.get_permissions_to_assign(
permission_set, process_related_path_segment, target_uris
)
)
return permissions_to_assign
@classmethod
def set_process_model_permissions(cls, target: str, permission_set: str) -> list[PermissionToAssign]:
permissions_to_assign: list[PermissionToAssign] = []
process_model_identifier = (
target.removeprefix("PM:").replace("/", ":").removeprefix(":")
)
process_related_path_segment = f"{process_model_identifier}/*"
if process_model_identifier == "ALL":
process_related_path_segment = "*"
target_uris = [f"/process-models/{process_related_path_segment}"]
permissions_to_assign = (
permissions_to_assign
+ cls.get_permissions_to_assign(
permission_set, process_related_path_segment, target_uris
)
)
return permissions_to_assign
@classmethod
def explode_permissions(
cls, permission_set: str, target: str
@ -654,72 +731,16 @@ class AuthorizationService:
permissions = ["create", "read", "update", "delete"]
if target.startswith("PG:"):
process_group_identifier = (
target.removeprefix("PG:").replace("/", ":").removeprefix(":")
)
process_related_path_segment = f"{process_group_identifier}:*"
if process_group_identifier == "ALL":
process_related_path_segment = "*"
target_uris = [
f"/process-groups/{process_related_path_segment}",
f"/process-models/{process_related_path_segment}",
]
permissions_to_assign = (
permissions_to_assign
+ cls.get_permissions_to_assign(
permission_set, process_related_path_segment, target_uris
)
)
permissions_to_assign += cls.set_process_group_permissions(target, permission_set)
elif target.startswith("PM:"):
process_model_identifier = (
target.removeprefix("PM:").replace("/", ":").removeprefix(":")
)
process_related_path_segment = f"{process_model_identifier}/*"
if process_model_identifier == "ALL":
process_related_path_segment = "*"
target_uris = [f"/process-models/{process_related_path_segment}"]
permissions_to_assign = (
permissions_to_assign
+ cls.get_permissions_to_assign(
permission_set, process_related_path_segment, target_uris
)
)
permissions_to_assign += cls.set_process_model_permissions(target, permission_set)
elif permission_set == "start":
raise InvalidPermissionError(
"Permission 'start' is only available for macros PM and PG."
)
elif target.startswith("BASIC"):
permissions_to_assign.append(
PermissionToAssign(
permission="read", target_uri="/process-instances/for-me"
)
)
permissions_to_assign.append(
PermissionToAssign(permission="read", target_uri="/processes")
)
permissions_to_assign.append(
PermissionToAssign(permission="read", target_uri="/service-tasks")
)
permissions_to_assign.append(
PermissionToAssign(
permission="read", target_uri="/user-groups/for-current-user"
)
)
for permission in ["create", "read", "update", "delete"]:
permissions_to_assign.append(
PermissionToAssign(
permission=permission, target_uri="/process-instances/reports/*"
)
)
permissions_to_assign.append(
PermissionToAssign(permission=permission, target_uri="/tasks/*")
)
permissions_to_assign += cls.set_basic_permissions()
elif target == "ALL":
for permission in permissions:
permissions_to_assign.append(

View File

@ -354,11 +354,7 @@ class BaseTest:
assert has_permission is expected_result
def modify_process_identifier_for_path_param(self, identifier: str) -> str:
"""Identifier."""
if "\\" in identifier:
raise Exception(f"Found backslash in identifier: {identifier}")
return identifier.replace("/", ":")
return ProcessModelInfo.modify_process_identifier_for_path_param(identifier)
def un_modify_modified_process_identifier_for_path_param(
self, modified_identifier: str

View File

@ -0,0 +1,51 @@
"""Test_users_controller."""
from flask.app import Flask
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec
from flask.testing import FlaskClient
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from spiffworkflow_backend.models.user import UserModel
class TestProcessInstancesController(BaseTest):
def test_find_by_id(
self,
app: Flask,
client: FlaskClient,
with_db_and_bpmn_file_cleanup: None,
with_super_admin_user: UserModel,
) -> None:
"""Test_user_search_returns_a_user."""
user_one = self.create_user_with_permission(username="user_one", target_uri="/process-instances/find-by-id/*")
user_two = self.create_user_with_permission(username="user_two", target_uri="/process-instances/find-by-id/*")
process_model = load_test_spec(
process_model_id="group/sample",
bpmn_file_name="sample.bpmn",
process_model_source_directory="sample",
)
process_instance = self.create_process_instance_from_process_model(
process_model=process_model, user=user_one
)
response = client.get(
f"/v1.0/process-instances/find-by-id/{process_instance.id}",
headers=self.logged_in_headers(user_one),
)
assert response.status_code == 200
assert response.json
assert response.json['id'] == process_instance.id
response = client.get(
f"/v1.0/process-instances/find-by-id/{process_instance.id}",
headers=self.logged_in_headers(user_two),
)
assert response.status_code == 400
response = client.get(
f"/v1.0/process-instances/find-by-id/{process_instance.id}",
headers=self.logged_in_headers(with_super_admin_user),
)
assert response.status_code == 200
assert response.json
assert response.json['id'] == process_instance.id

View File

@ -277,6 +277,7 @@ class TestAuthorizationService(BaseTest):
) -> None:
"""Test_explode_permissions_basic."""
expected_permissions = [
("/process-instances/find-by-id/*", "read"),
("/process-instances/for-me", "read"),
("/process-instances/reports/*", "create"),
("/process-instances/reports/*", "delete"),