Skip to content

Commit

Permalink
feat(story): u#3725 t#4071 prev and next story of old workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
tdelatorre committed Oct 16, 2023
1 parent 9740e44 commit 0b78fbb
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 7 deletions.
25 changes: 21 additions & 4 deletions python/apps/taiga/src/taiga/stories/stories/services/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from uuid import UUID

from taiga.base.api import Pagination
from taiga.base.repositories.neighbors import Neighbor
from taiga.projects.projects.models import Project
from taiga.stories.stories import events as stories_events
from taiga.stories.stories import repositories as stories_repositories
Expand Down Expand Up @@ -112,7 +113,9 @@ async def get_story(project_id: UUID, ref: int) -> Story | None:
)


async def get_story_detail(project_id: UUID, ref: int) -> StoryDetailSerializer:
async def get_story_detail(
project_id: UUID, ref: int, neighbors: Neighbor[Story] | None = None
) -> StoryDetailSerializer:
story = cast(
Story,
await stories_repositories.get_story(
Expand All @@ -129,7 +132,12 @@ async def get_story_detail(project_id: UUID, ref: int) -> StoryDetailSerializer:
prefetch_related=["assignees"],
),
)
neighbors = await stories_repositories.list_story_neighbors(story=story, filters={"workflow_id": story.workflow_id})

if not neighbors:
neighbors = await stories_repositories.list_story_neighbors(
story=story, filters={"workflow_id": story.workflow_id}
)

assignees = await stories_repositories.list_story_assignees(story=story)

return serializers_services.serialize_story_detail(story=story, neighbors=neighbors, assignees=assignees)
Expand All @@ -145,8 +153,17 @@ async def update_story(
current_version: int,
values: dict[str, Any] = {},
) -> StoryDetailSerializer:
# Update story
# Values to update
update_values = await _validate_and_process_values_to_update(story, values)

# Old neighbors
old_neighbors = None
if update_values.get("workflow", None):
old_neighbors = await stories_repositories.list_story_neighbors(
story=story, filters={"workflow_id": story.workflow_id}
)

# Update story
if not await stories_repositories.update_story(
id=story.id,
current_version=current_version,
Expand All @@ -155,7 +172,7 @@ async def update_story(
raise ex.UpdatingStoryWithWrongVersionError("Updating a story with the wrong version.")

# Get detailed story
detailed_story = await get_story_detail(project_id=story.project_id, ref=story.ref)
detailed_story = await get_story_detail(project_id=story.project_id, ref=story.ref, neighbors=old_neighbors)

# Emit event
await stories_events.emit_event_when_story_is_updated(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,76 @@ async def test_update_story_ok():
current_version=story.version,
values=values,
)
fake_get_story_detail.assert_awaited_once_with(project_id=story.project_id, ref=story.ref, neighbors=None)
fake_stories_events.emit_event_when_story_is_updated.assert_awaited_once_with(
project=story.project,
story=updated_story,
updates_attrs=[*values],
)
assert updated_story == detailed_story


async def test_update_story_workflow_ok():
project = f.build_project()
old_workflow = f.build_workflow(project=project)
workflow_status1 = f.build_workflow_status(workflow=old_workflow)
workflow_status2 = f.build_workflow_status(workflow=old_workflow)
story1 = f.build_story(project=project, workflow=old_workflow, status=workflow_status1)
story2 = f.build_story(project=project, workflow=old_workflow, status=workflow_status1)
story3 = f.build_story(project=project, workflow=old_workflow, status=workflow_status2)
new_workflow = f.build_workflow(project=project)
workflow_status3 = f.build_workflow_status(workflow=new_workflow)
values = {
"version": story2.version + 1,
"workflow": new_workflow,
"status": workflow_status3,
"order": services.DEFAULT_ORDER_OFFSET + story2.order,
}
old_neighbors = {
"prev": {"ref": story1.ref, "title": story1.title},
"next": {"ref": story3.ref, "title": story3.title},
}
detailed_story = {
"ref": story2.ref,
"version": story2.version + 1,
"workflow": {"id": new_workflow.id, "name": new_workflow.name, "slug": new_workflow.slug},
"prev": {"ref": story1.ref, "title": story1.title},
"next": {"ref": story3.ref, "title": story3.title},
}

with (
patch("taiga.stories.stories.services.stories_repositories", autospec=True) as fake_stories_repo,
patch(
"taiga.stories.stories.services._validate_and_process_values_to_update", autospec=True
) as fake_validate_and_process,
patch("taiga.stories.stories.services.get_story_detail", autospec=True) as fake_get_story_detail,
patch("taiga.stories.stories.services.stories_events", autospec=True) as fake_stories_events,
):
fake_validate_and_process.return_value = values
fake_stories_repo.list_story_neighbors.return_value = old_neighbors
fake_stories_repo.update_story.return_value = True
fake_get_story_detail.return_value = detailed_story

updated_story = await services.update_story(
story=story2,
current_version=story2.version,
values=values,
)

fake_validate_and_process.assert_awaited_once_with(
story=story2,
values=values,
)
fake_stories_repo.update_story.assert_awaited_once_with(
id=story2.id,
current_version=story2.version,
values=values,
)
fake_get_story_detail.assert_awaited_once_with(
project_id=story.project_id,
ref=story.ref,
project_id=story2.project_id, ref=story2.ref, neighbors=old_neighbors
)
fake_stories_events.emit_event_when_story_is_updated.assert_awaited_once_with(
project=story.project,
project=story2.project,
story=updated_story,
updates_attrs=[*values],
)
Expand Down

1 comment on commit 0b78fbb

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/taiga
   __main__.py71710%17–155
src/taiga/attachments
   admin.py27390%25, 28, 70
   events.py8275%15, 20
   models.py19289%61, 64
   repositories.py49197%62
src/taiga/auth/services
   __init__.py47098%90–>93
src/taiga/base
   mocks.py741280%93–94, 107–118, 143–>exit
src/taiga/base/api
   permissions.py49199%22
   requests.py11380%15, 21–23
src/taiga/base/db
   __init__.py4175%15
   commands.py54540%8–271
   sequences.py45297%91–92
src/taiga/base/db/admin
   forms.py14838%17–25
   utils.py11736%17–23
src/taiga/base/db/models
   fields.py15188%20
src/taiga/base/django
   __init__.py5260%17–19
   settings.py30293%16–17
   urls.py330%8–11
src/taiga/base/i18n
   __init__.py102988%171–>170, 173–>170, 192–193, 203–205, 215, 268, 279, 283
   choices.py34291%80, 87
   commands.py74740%8–108
src/taiga/base/logging
   formatters.py241050%22–>exit, 38, 44–45, 51–60
   middlewares.py23196%20
src/taiga/base/repositories
   neighbors.py39391%43–44, 65
src/taiga/base/sampledata
   commands.py11110%8–40
   constants.py15150%14–51
   demo_data.py2732730%8–529
   factories.py1221220%9–353
   test_data.py2902900%8–634
src/taiga/base/serializers
   fields.py20196%21
src/taiga/base/utils
   commands.py31310%8–60
   datetime.py441860%54, 67, 126–147
   enum.py14192%43
   images.py26387%21–22, 30
   json.py331545%30–35, 37, 39–44, 46, 50
   pprint.py660%8–17
   slug.py23485%22–25
src/taiga/base/validators/fields
   i18n.py16291%19–20
   uuid.py15290%19–20
src/taiga/comments
   admin.py29392%16, 20, 24
   events.py11373%15, 20, 25
   models.py20291%43, 46
src/taiga/commons/storage
   commands.py13130%8–40
   models.py16288%38, 41
   services.py9092%17–>16
   tasks.py990%8–19
src/taiga/conf
   __init__.py94395%112, 128–129
   tokens.py17190%34
src/taiga/emails
   commands.py35350%9–68
   sender.py18571%48–50, 54–73
   tasks.py39490%44–45, 53–54
src/taiga/events
   app.py18192%21
   events.py16288%31–32
   logging.py392331%18–>exit, 32–48, 51–60
   manager.py1171190%78–79, 223–233, 239–242
   responses.py33684%33, 37–41
   subscriber.py80495%51, 55, 88, 91, 108–>120
src/taiga/events/actions
   auth.py231144%27–32, 39–45
   event_handlers.py9456%18–20, 28–30
   projects.py553038%36–40, 48–64, 72–80, 88–96
   workspaces.py553038%36–40, 48–64, 72–80, 88–96
src/taiga/events/pubsub/backends
   base.py30198%53
   redis.py66594%41–43, 85–86
src/taiga/exceptions/api
   __init__.py27097%28–>30
   handlers.py27290%27–28
   middlewares.py26287%37, 41
src/taiga/integrations/auth
   services.py39782%50–56
src/taiga/integrations/github
   services.py40098%63–>68
src/taiga/mediafiles
   admin.py20291%24, 27
   models.py26292%84, 87
src/taiga/permissions
   services.py103198%28, 106–>109
src/taiga/permissions/validators
   fields.py24388%18–19, 45
src/taiga/projects/invitations
   models.py25292%89, 92
   repositories.py84198%135
src/taiga/projects/invitations/api
   validators.py65789%38–40, 65–66, 84–85
src/taiga/projects/invitations/events
   __init__.py33191%37–>exit, 49–>exit, 74–>exit, 102
src/taiga/projects/invitations/services
   __init__.py183199%58–>53, 286
src/taiga/projects/memberships
   models.py16288%51, 54
   repositories.py86297%91, 124
src/taiga/projects/memberships/events
   __init__.py16288%18–24
src/taiga/projects/memberships/services
   __init__.py45195%64–>69, 111
src/taiga/projects/projects
   admin.py531275%33–34, 39–42, 50–51, 56–59, 90, 94
   models.py61886%64, 67, 78, 113, 116, 119–121
   repositories.py115496%125, 126–>129, 155, 254–255
   tasks.py6188%17
src/taiga/projects/projects/services
   __init__.py107592%78, 87, 199, 205–>208, 213, 235
src/taiga/projects/references
   mixins.py13093%25–>28
src/taiga/projects/roles
   api.py35393%46–48
   models.py26386%50, 53, 57
   repositories.py44196%92
src/taiga/projects/roles/services
   __init__.py25194%24, 57–>62
src/taiga/stories/assignments
   models.py15287%42, 45
   repositories.py52197%49
src/taiga/stories/attachments
   api.py51197%159
   serializers.py11093%20–>28
src/taiga/stories/comments
   api.py58197%177
src/taiga/stories/stories
   admin.py25584%76–81, 85, 89
   models.py25292%73, 76
src/taiga/stories/stories/services
   __init__.py131297%210–>226, 270, 293
src/taiga/tasksqueue
   app.py20286%29–30
   commands.py65650%8–122
   logging.py331740%18–>exit, 29–39, 42–51
   manager.py561377%37, 41, 45, 49, 94, 163, 177, 186, 191, 198, 202, 209, 213
   task.py12192%23
src/taiga/tokens
   admin.py441081%67, 70, 73, 76, 104, 108, 112, 116, 120, 124
   models.py30487%59, 62, 78, 81
src/taiga/users
   admin.py51781%32, 41, 50, 87–95
   commands.py990%8–26
   models.py651086%71, 74, 90, 93, 97, 100, 103, 106, 127, 130
   repositories.py204398%209–>212, 335, 428–429
src/taiga/users/api
   __init__.py66296%216, 304
src/taiga/users/api/validators
   __init__.py42392%39–41
src/taiga/users/services
   __init__.py244795%75, 259, 262–>257, 297–>280, 306–>280, 322–>318, 345–>341, 366, 494–>499, 513–520, 521–>526, 524–525
src/taiga/workflows
   models.py40490%42, 45, 85, 88
src/taiga/workflows/services
   __init__.py113494%53, 232, 259, 330–>338, 345
src/taiga/workspaces/invitations
   models.py24292%84, 87
   permissions.py9182%19
   repositories.py77198%123
src/taiga/workspaces/invitations/api
   validators.py18288%24–25
src/taiga/workspaces/invitations/events
   __init__.py25191%36–>exit, 60–>exit, 71
src/taiga/workspaces/invitations/services
   __init__.py123199%222
src/taiga/workspaces/memberships
   api.py43196%126
   models.py15287%43, 46
   repositories.py63295%72, 105
src/taiga/workspaces/memberships/services
   __init__.py46197%117
src/taiga/workspaces/workspaces
   admin.py571962%25–26, 31–34, 49, 52–57, 61, 65, 73–74, 79–82
   models.py18290%32, 35
   repositories.py127299%232–233
src/taiga/workspaces/workspaces/services
   __init__.py54294%113, 116
TOTAL9911156683% 

Tests Skipped Failures Errors Time
1147 1 💤 0 ❌ 0 🔥 23m 29s ⏱️

Please sign in to comment.