Skip to content

Commit bbd69f8

Browse files
committed
FWF-4504: [Feature] Task outcome configuration model and APIs
1 parent 4a69674 commit bbd69f8

File tree

7 files changed

+69
-43
lines changed

7 files changed

+69
-43
lines changed

forms-flow-api/migrations/versions/ac75256dc5c9_task_outcome_configuration_table.py renamed to forms-flow-api/migrations/versions/716e21e9864f_task_outcome_configuration_table.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
"""Task_outcome_configuration_table
22
3-
Revision ID: 47d590c1d289
3+
Revision ID: 716e21e9864f
44
Revises: a5d9bbf7b5ac
5-
Create Date: 2025-05-05 17:50:42.349349
5+
Create Date: 2025-05-06 16:50:10.473682
66
77
"""
88
from alembic import op
99
import sqlalchemy as sa
10-
from sqlalchemy.dialects import postgresql
10+
1111

1212
# revision identifiers, used by Alembic.
13-
revision = 'ac75256dc5c9'
13+
revision = '716e21e9864f'
1414
down_revision = 'a5d9bbf7b5ac'
1515
branch_labels = None
1616
depends_on = None
@@ -21,8 +21,10 @@ def upgrade():
2121
op.create_table('task_outcome_configuration',
2222
sa.Column('id', sa.Integer(), nullable=False),
2323
sa.Column('task_id', sa.String(length=100), nullable=False, comment='Task ID'),
24-
sa.Column('task_outcome', postgresql.ARRAY(sa.JSON()), nullable=True, comment='Task outcome'),
25-
sa.Column('created_by', sa.String(length=100), nullable=True, comment='Created by'),
24+
sa.Column('task_name', sa.String(length=100), nullable=True, comment='Task name'),
25+
sa.Column('task_transition_map', sa.JSON(), nullable=True, comment='Task transition map'),
26+
sa.Column('transition_map_type', sa.String(length=100), nullable=True, comment='Task transition map type'),
27+
sa.Column('created_by', sa.String(length=100), nullable=False, comment='Created by'),
2628
sa.Column('tenant', sa.String(length=100), nullable=True, comment='Tenant key'),
2729
sa.Column('created', sa.DateTime(timezone=True), nullable=False),
2830
sa.PrimaryKeyConstraint('id')

forms-flow-api/src/formsflow_api/constants/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ class BusinessErrorCode(ErrorCodeMixin, Enum):
138138
"Database error while updating filter preferences",
139139
HTTPStatus.BAD_REQUEST,
140140
)
141+
TASK_OUTCOME_NOT_FOUND = (
142+
"Task outcome configuration not found for the given task Id",
143+
HTTPStatus.BAD_REQUEST,
144+
)
141145

142146
def __new__(cls, message, status_code):
143147
"""Constructor."""

forms-flow-api/src/formsflow_api/models/tasks.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from __future__ import annotations
44

55
from sqlalchemy import JSON
6-
from sqlalchemy.dialects.postgresql import ARRAY
76

87
from .audit_mixin import ApplicationAuditDateTimeMixin
98
from .base_model import BaseModel
@@ -17,27 +16,33 @@ class TaskOutcomeConfiguration(ApplicationAuditDateTimeMixin, BaseModel, db.Mode
1716
task_id = db.Column(
1817
db.String(100), nullable=False, comment="Task ID", unique=True, index=True
1918
)
20-
task_outcome = db.Column(ARRAY(JSON), nullable=True, comment="Task outcome")
21-
created_by = db.Column(db.String(100), nullable=True, comment="Created by")
19+
task_name = db.Column(db.String(100), nullable=True, comment="Task name")
20+
task_transition_map = db.Column(JSON, nullable=True, comment="Task transition map")
21+
transition_map_type = db.Column(
22+
db.String(100), nullable=True, comment="Task transition map type"
23+
)
24+
created_by = db.Column(db.String(100), nullable=False, comment="Created by")
2225
tenant = db.Column(db.String(100), nullable=True, comment="Tenant key", index=True)
2326

2427
@classmethod
2528
def create_from_dict(
2629
cls, task_outcome_info: dict
2730
) -> TaskOutcomeConfiguration | None:
28-
"""Create new task outcome."""
31+
"""Create new task outcome configuration."""
2932
if task_outcome_info:
3033
task = TaskOutcomeConfiguration()
3134
task.created_by = task_outcome_info.get("created_by")
3235
task.task_id = task_outcome_info.get("task_id")
33-
task.task_outcome = task_outcome_info.get("task_outcome")
36+
task.task_name = task_outcome_info.get("task_name")
37+
task.task_transition_map = task_outcome_info.get("task_transition_map")
38+
task.transition_map_type = task_outcome_info.get("transition_map_type")
3439
task.tenant = task_outcome_info.get("tenant")
3540
task.save()
3641
return task
3742
return None
3843

3944
@classmethod
40-
def get_task_outcome_by_task_id(
45+
def get_task_outcome_configuration_by_task_id(
4146
cls, task_id: str, tenant: str
4247
) -> TaskOutcomeConfiguration | None:
4348
"""Get task outcome configuration by task ID."""

forms-flow-api/src/formsflow_api/resources/tasks.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,23 @@
1818
"TaskOutcomeRequest",
1919
{
2020
"taskId": fields.String(description="Task ID", required=True),
21-
"taskOutcome": fields.List(
22-
fields.Raw(description="Task outcome"), required=True, min_items=1
21+
"taskName": fields.String(
22+
description="Task name", required=True, allow_none=True
23+
),
24+
"transitionMapType": fields.String(
25+
description="Task transition map type - select/input/radio", required=True
26+
),
27+
"taskTransitionMap": fields.Raw(
28+
description="Determines the next step in workflow", required=True
2329
),
2430
},
2531
)
2632

27-
task_outcome_response = API.model(
33+
task_outcome_response = API.inherit(
2834
"TaskOutcomeResponse",
35+
task_outcome_request,
2936
{
30-
"id": fields.Integer(description="Task outcome ID"),
31-
"taskId": fields.String(description="Task ID"),
32-
"taskOutcome": fields.List(
33-
fields.Raw(description="Task outcome"), required=True, min_items=1
34-
),
37+
"id": fields.Integer(description="Task outcome configuration ID"),
3538
"createdBy": fields.String(description="Created by"),
3639
"tenant": fields.String(description="Tenant key"),
3740
"created": fields.DateTime(description="Created date"),
@@ -40,7 +43,7 @@
4043

4144

4245
@cors_preflight("POST, OPTIONS")
43-
@API.route("/task-outcome", methods=["POST", "OPTIONS"])
46+
@API.route("/task-outcome-configuration", methods=["POST", "OPTIONS"])
4447
class TaskOutcomeResource(Resource):
4548
"""Resource to create task outcome configuration."""
4649

@@ -60,12 +63,12 @@ def post():
6063
data = request.get_json()
6164
if not data:
6265
return {"message": "Invalid input"}, HTTPStatus.BAD_REQUEST
63-
response = TaskService().create_task_outcome(data)
66+
response = TaskService().create_task_outcome_configuration(data)
6467
return response, HTTPStatus.CREATED
6568

6669

6770
@cors_preflight("GET, OPTIONS")
68-
@API.route("/task-outcome/<string:task_id>", methods=["GET", "OPTIONS"])
71+
@API.route("/task-outcome-configuration/<string:task_id>", methods=["GET", "OPTIONS"])
6972
@API.param("task_id", "Task ID")
7073
class TaskOutcomeByIdResource(Resource):
7174
"""Resource to get task outcome configuration by task ID."""
@@ -83,5 +86,5 @@ class TaskOutcomeByIdResource(Resource):
8386
)
8487
def get(task_id: str):
8588
"""Get task outcome configuration by task ID."""
86-
response = TaskService().get_task_outcome(task_id)
89+
response = TaskService().get_task_outcome_configuration(task_id)
8790
return response, HTTPStatus.OK

forms-flow-api/src/formsflow_api/schemas/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,5 @@
4747
)
4848
from .process_history_logs import ProcessHistorySchema
4949
from .roles import RolesGroupsSchema
50-
from .tasks import TaskOutcomeSchema
50+
from .tasks import TaskOutcomeConfigurationSchema
5151
from .theme import ThemeCustomizationSchema

forms-flow-api/src/formsflow_api/schemas/tasks.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@
55
from .base_schema import AuditDateTimeSchema
66

77

8-
class TaskOutcomeSchema(AuditDateTimeSchema):
8+
class TaskOutcomeConfigurationSchema(AuditDateTimeSchema):
99
"""This class manages task outcome configuration schema."""
1010

1111
class Meta: # pylint: disable=too-few-public-methods
1212
"""Exclude unknown fields in the deserialized output."""
1313

1414
unknown = EXCLUDE
1515

16-
id = fields.Int()
16+
id = fields.Int(dump_only=True)
1717
tenant = fields.Str(dump_only=True)
1818
task_id = fields.Str(data_key="taskId", required=True)
19-
task_outcome = fields.List(fields.Dict(), data_key="taskOutcome", required=True)
19+
task_name = fields.Str(data_key="taskName", required=True, allow_none=True)
20+
task_transition_map = fields.Raw(data_key="taskTransitionMap", required=True)
21+
transition_map_type = fields.Str(data_key="transitionMapType", required=True)
2022
created_by = fields.Str(data_key="createdBy", dump_only=True)
Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,66 @@
11
"""This exposes tasks service."""
22

33
from flask import current_app
4+
from formsflow_api_utils.exceptions import BusinessException
45
from formsflow_api_utils.utils.user_context import UserContext, user_context
56

7+
from formsflow_api.constants import BusinessErrorCode
68
from formsflow_api.models.tasks import TaskOutcomeConfiguration
7-
from formsflow_api.schemas.tasks import TaskOutcomeSchema
9+
from formsflow_api.schemas.tasks import TaskOutcomeConfigurationSchema
810

9-
task_outcome_schema = TaskOutcomeSchema()
11+
task_outcome_schema = TaskOutcomeConfigurationSchema()
1012

1113

1214
class TaskService:
1315
"""This class manages task service."""
1416

1517
@user_context
16-
def create_task_outcome(
18+
def create_task_outcome_configuration(
1719
self, request_data: dict, **kwargs
1820
) -> TaskOutcomeConfiguration | None:
1921
"""Create new task outcome."""
2022
current_app.logger.info("Creating task outcome configuration")
2123
user: UserContext = kwargs["user"]
2224
data = task_outcome_schema.load(request_data)
23-
task_outcome = TaskOutcomeConfiguration.get_task_outcome_by_task_id(
24-
data["task_id"], user.tenant_key
25+
task_outcome_config = (
26+
TaskOutcomeConfiguration.get_task_outcome_configuration_by_task_id(
27+
data["task_id"], user.tenant_key
28+
)
2529
)
26-
if task_outcome:
30+
if task_outcome_config:
2731
current_app.logger.info("Task outcome configuration already exists")
28-
task_outcome.task_outcome = data["task_outcome"]
32+
task_outcome_config.task_name = data["task_name"]
33+
task_outcome_config.task_transition_map = data["task_transition_map"]
34+
task_outcome_config.transition_map_type = data["transition_map_type"]
2935
else:
30-
task_outcome = TaskOutcomeConfiguration(
36+
task_outcome_config = TaskOutcomeConfiguration(
3137
task_id=data["task_id"],
32-
task_outcome=data["task_outcome"],
38+
task_name=data["task_name"],
39+
task_transition_map=data["task_transition_map"],
40+
transition_map_type=data["transition_map_type"],
3341
created_by=user.user_name,
3442
tenant=user.tenant_key,
3543
)
36-
task_outcome.save()
44+
task_outcome_config.save()
3745
current_app.logger.info("Task outcome configuration created successfully")
38-
response = task_outcome_schema.dump(task_outcome)
46+
response = task_outcome_schema.dump(task_outcome_config)
3947
return response
4048

4149
@user_context
42-
def get_task_outcome(
50+
def get_task_outcome_configuration(
4351
self, task_id: str, **kwargs
4452
) -> TaskOutcomeConfiguration | None:
4553
"""Get task outcome configuration by task ID."""
4654
current_app.logger.info(
4755
"Getting task outcome configuration for task ID {task_id}"
4856
)
4957
user: UserContext = kwargs["user"]
50-
task_outcome = TaskOutcomeConfiguration.get_task_outcome_by_task_id(
51-
task_id, user.tenant_key
58+
task_outcome = (
59+
TaskOutcomeConfiguration.get_task_outcome_configuration_by_task_id(
60+
task_id, user.tenant_key
61+
)
5262
)
5363
if task_outcome:
5464
response = task_outcome_schema.dump(task_outcome)
5565
return response
56-
return None
66+
raise BusinessException(BusinessErrorCode.TASK_OUTCOME_NOT_FOUND)

0 commit comments

Comments
 (0)