Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add iteration work items #29

Merged
merged 1 commit into from
Jun 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Add iteration work items
  • Loading branch information
timmo001 committed Jun 15, 2024
commit 065fc47d6b4d1d5885803a53f44bf3e4cd912bcb
35 changes: 35 additions & 0 deletions aioazuredevops/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
VersionControl,
)
from .models.iteration import Iteration, IterationAttributes
from .models.iteration_work_item import (
IterationWorkItemsResult,
WorkItemRelation,
WorkItemRelationTarget,
)
from .models.wiql import WIQLColumn, WIQLResult, WIQLWorkItem
from .models.work_item import (
WorkItem,
Expand Down Expand Up @@ -370,6 +375,36 @@ async def get_iteration(
url=iteration["url"],
)

async def get_iteration_work_items(
self,
organization: str,
project: str,
iteration_id: str,
) -> IterationWorkItemsResult | None:
"""Get Azure DevOps iteration work items."""
response: aiohttp.ClientResponse = await self._get(
f"{DEFAULT_BASE_URL}/{organization}/{project}/_apis/work/teamsettings/iterations/{iteration_id}/workitems?api-version={DEFAULT_API_VERSION}"
)
if response.status != 200:
return None
if (data := await response.json()) is None:
return None

return IterationWorkItemsResult(
work_item_relations=[
WorkItemRelation(
rel=work_item_relation.get("rel", None),
source=work_item_relation.get("source", None),
target=WorkItemRelationTarget(
id=work_item_relation["target"]["id"],
url=work_item_relation["target"]["url"],
),
)
for work_item_relation in data["workItemRelations"]
],
url=data["url"],
)

async def get_work_item_ids_from_wiql(
self,
organization: str,
Expand Down
29 changes: 29 additions & 0 deletions aioazuredevops/models/iteration_work_item.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Model for Iteration Work Item."""

from dataclasses import dataclass
from typing import Any


@dataclass
class WorkItemRelationTarget:
"""Work item relation target."""

id: int
url: str


@dataclass
class WorkItemRelation:
"""Work item relation."""

rel: Any
source: Any
target: WorkItemRelationTarget


@dataclass
class IterationWorkItemsResult:
"""Iteration work items result."""

work_item_relations: list[WorkItemRelation]
url: str
7 changes: 7 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@
"value": [RESPONSE_JSON_DEVOPS_ITERATION],
}

RESPONSE_JSON_DEVOPS_ITERATION_WORK_ITEMS: Final[dict] = {
"workItemRelations": [
{"rel": None, "source": None, "target": {"id": 1, "url": "testurl"}}
],
"url": "testurl",
}

RESPONSE_JSON_DEVOPS_WIQL_RESULT: Final[dict] = {
"queryType": "testqueryType",
"queryResultType": "testqueryResultType",
Expand Down
3 changes: 3 additions & 0 deletions tests/__snapshots__/test_client.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
# name: test_get_iteration[iteration]
Iteration(id='abc123', name='testname', path='testname\\Sprint 1', attributes=IterationAttributes(start_date='2021-01-01T00:00:00Z', finish_date='2021-01-31T00:00:00Z', time_frame='current'), url='testurl')
# ---
# name: test_get_iteration_work_items[iteration_work_items]
IterationWorkItemsResult(work_item_relations=[WorkItemRelation(rel=None, source=None, target=WorkItemRelationTarget(id=1, url='testurl'))], url='testurl')
# ---
# name: test_get_iterations[iterations]
list([
Iteration(id='abc123', name='testname', path='testname\\Sprint 1', attributes=IterationAttributes(start_date='2021-01-01T00:00:00Z', finish_date='2021-01-31T00:00:00Z', time_frame='current'), url='testurl'),
Expand Down
7 changes: 7 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
RESPONSE_JSON_DEVOPS_BUILD,
RESPONSE_JSON_DEVOPS_BUILDS,
RESPONSE_JSON_DEVOPS_ITERATION,
RESPONSE_JSON_DEVOPS_ITERATION_WORK_ITEMS,
RESPONSE_JSON_DEVOPS_ITERATIONS,
RESPONSE_JSON_DEVOPS_PROJECT,
RESPONSE_JSON_DEVOPS_WIQL_RESULT,
Expand Down Expand Up @@ -88,6 +89,12 @@ def mock_aioresponse():
status=200,
repeat=True,
)
mocker.get(
f"{DEFAULT_BASE_URL}/{ORGANIZATION}/{PROJECT}/_apis/work/teamsettings/iterations/abc123/workitems?api-version={DEFAULT_API_VERSION}",
payload=RESPONSE_JSON_DEVOPS_ITERATION_WORK_ITEMS,
status=200,
repeat=True,
)
mocker.post(
f"{DEFAULT_BASE_URL}/{ORGANIZATION}/{PROJECT}/_apis/wit/wiql?api-version={DEFAULT_API_VERSION}",
payload=RESPONSE_JSON_DEVOPS_WIQL_RESULT,
Expand Down
47 changes: 47 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,53 @@ async def test_get_iteration(
assert empty_iteration is None


@pytest.mark.asyncio
async def test_get_iteration_work_items(
devops_client: DevOpsClient,
mock_aioresponse: aioresponses,
snapshot: SnapshotAssertion,
) -> None:
"""Test the get_iteration_work_items method."""
iteration_work_items = await devops_client.get_iteration_work_items(
organization=ORGANIZATION,
project=PROJECT,
iteration_id="abc123",
)

assert iteration_work_items == snapshot(
name="iteration_work_items",
)

# Test with bad request
mock_aioresponse.get(
f"{DEFAULT_BASE_URL}/{ORGANIZATION}/{BAD_PROJECT_NAME}/_apis/work/teamsettings/iterations/abc123/workitems?api-version={DEFAULT_API_VERSION}",
status=400,
)

bad_iteration_work_items = await devops_client.get_iteration_work_items(
organization=ORGANIZATION,
project=BAD_PROJECT_NAME,
iteration_id="abc123",
)

assert bad_iteration_work_items is None

# Test with empty response
mock_aioresponse.get(
f"{DEFAULT_BASE_URL}/{ORGANIZATION}/{EMPTY_PROJECT_NAME}/_apis/work/teamsettings/iterations/abc123/workitems?api-version={DEFAULT_API_VERSION}",
payload=None,
status=200,
)

empty_iteration_work_items = await devops_client.get_iteration_work_items(
organization=ORGANIZATION,
project=EMPTY_PROJECT_NAME,
iteration_id="abc123",
)

assert empty_iteration_work_items is None


@pytest.mark.asyncio
async def test_get_work_items_ids(
devops_client: DevOpsClient,
Expand Down
Loading