Skip to content

Commit

Permalink
add enter_automatically option to osim state
Browse files Browse the repository at this point in the history
Signed-off-by: Conrado Costa <[email protected]>
  • Loading branch information
costaconrado committed Oct 24, 2023
1 parent da42da7 commit b3e7df3
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 3 deletions.
4 changes: 4 additions & 0 deletions apps/osim/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ class CheckParser(metaclass=MetaCheckParser):
"is_major_incident": "is_major_incident_temp",
"cve": "cve_id",
"cwe": "cwe_id",
"trackers": "trackers_filed",
"owner": "task_owner",
"team": "task_team_id",
"state": "osim_state",
}

@classmethod
Expand Down
4 changes: 4 additions & 0 deletions apps/osim/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ class APIError(OSIMException):

class WorkflowDefinitionError(OSIMException):
"""exception class for workflow definitions errors"""


class StateNotFoundException(OSIMException):
"""exception class for trying to get a non-existent state"""
28 changes: 26 additions & 2 deletions apps/osim/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ def __init__(self, state_desc):
self.requirements = [
Check(requirement_desc) for requirement_desc in state_desc["requirements"]
]
# enter_automatically defaults to true to preserve previous behaviour
self.enter_automatically = state_desc.get("enter_automatically", True)
if not state_desc.get("enter_automatically", True):
self.requirements.append(Check(f"state equals {self.name}"))

def accepts(self, instance):
"""accepts a given instance if it meets all the requirements"""
Expand Down Expand Up @@ -77,6 +81,9 @@ def __lt__(self, other):

def accepts(self, instance):
"""accepts the instance if it meets all the conditions"""
# Rejects manually classified instances from another workflow
if instance.manually_classified and self.name != instance.osim_workflow:
return False
return all(condition(instance) for condition in self.conditions)

def classify(self, instance):
Expand All @@ -87,10 +94,27 @@ def classify(self, instance):
the initial state is required to have the empty requirements
so there is always an applicable state to classify the instance in
in case instance was manually classified it can only go forward automatically
"""

last = None
current = None
is_manually_classified = instance.is_manually_classified
rejected = False
for state in self.states:
if state.name == instance.osim_state:
current = state
if not state.accepts(instance):
break
rejected = True
# manual classifications can only go forward automatically
if not is_manually_classified or current:
return last
last = state
return last

if rejected:
# manual state is not present in workflow; return first
return self.states[0] if not current else current
else:
# passed in all validations; return last
return last
52 changes: 52 additions & 0 deletions apps/osim/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,58 @@ def test_classify_complete(self):
assert_workflow_equals(classified_workflow, workflow_reject)
assert_state_equals(classified_state, state_not_affected)

def test_manual_action_conditions(self):
"""
test that workflows requiring manual transition can be validated
"""
state_new = {
"name": WorkflowModel.OSIMState.NEW,
"enter_automatically": True,
"requirements": [],
}

state_triage = {
"name": WorkflowModel.OSIMState.TRIAGE,
"enter_automatically": True,
"requirements": ["has cwe"],
}

state_pre_second = {
"name": WorkflowModel.OSIMState.PRE_SECOND_ASSESSMENT,
"enter_automatically": False,
"requirements": [],
}

workflow_default = Workflow(
{
"name": WorkflowModel.OSIMState.NEW,
"description": "a test workflow",
"priority": 0,
"conditions": [],
"states": [state_new, state_triage, state_pre_second],
}
)

workflow_framework = WorkflowFramework()
workflow_framework.register_workflow(workflow_default)

flaw = FlawFactory(cwe_id="")
_, classified_state = workflow_framework.classify(flaw)
assert_state_equals(classified_state, state_new)

flaw.cwe_id = "CWE-1"
_, classified_state = workflow_framework.classify(flaw)
assert_state_equals(classified_state, state_triage)

flaw.osim_state = WorkflowModel.OSIMState.PRE_SECOND_ASSESSMENT
_, classified_state = workflow_framework.classify(flaw)
assert_state_equals(classified_state, state_pre_second)

# Tests that a state with enter_automatically: false does not leave from the state automatically
flaw.cwe_id = ""
_, classified_state = workflow_framework.classify(flaw)
assert_state_equals(classified_state, state_pre_second)


class TestFlaw:
def test_init(self):
Expand Down
21 changes: 20 additions & 1 deletion apps/osim/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from django.db import models

from .constants import WORKFLOW_DIR
from .exceptions import WorkflowDefinitionError
from .exceptions import StateNotFoundException, WorkflowDefinitionError
from .helpers import singleton
from .models import Workflow

Expand Down Expand Up @@ -89,6 +89,17 @@ def classify(self, instance, state=True):
if workflow.accepts(instance):
return (workflow, workflow.classify(instance)) if state else workflow

def get_enter_automatically(self, workflow_name, state_name):
for workflow in self._workflows:
if workflow.name == workflow_name:
for state in workflow.states:
if state.name == state_name:
return state.enter_automatically

raise StateNotFoundException(
f"Combination of workflow and state ({workflow_name}, {state_name}) is not present in framework."
)


class WorkflowModel(models.Model):
"""workflow model base class"""
Expand Down Expand Up @@ -126,6 +137,14 @@ def classify(self):
"state": state.name,
}

@property
def is_manually_classified(self):
if not self.osim_workflow:
return False
return not WorkflowFramework().get_enter_automatically(
self.osim_workflow, self.osim_state
)

@property
def classification(self):
"""stored workflow classification"""
Expand Down

0 comments on commit b3e7df3

Please sign in to comment.