diff --git a/generator_config.yml b/generator_config.yml index 38c1cdd..6224539 100644 --- a/generator_config.yml +++ b/generator_config.yml @@ -1,6 +1,5 @@ project_name_override: taskbadger-python package_name_override: taskbadger/internal post_hooks: - - "autoflake -i -r --remove-all-unused-imports --remove-unused-variables --ignore-init-module-imports ." - - "isort ." - - "black ." + - "ruff format ." + - "ruff check . --fix" diff --git a/pyproject.toml b/pyproject.toml index 0136893..c9aa2d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -95,3 +95,6 @@ quote-style = "double" indent-style = "space" skip-magic-trailing-comma = false line-ending = "auto" + +[tool.ruff.lint.per-file-ignores] +"taskbadger/internal/*" = ["E501"] diff --git a/taskbadger.yaml b/taskbadger.yaml index 1d262c1..6a8387a 100644 --- a/taskbadger.yaml +++ b/taskbadger.yaml @@ -47,20 +47,23 @@ paths: examples: ListResponse: value: - previous: string - next: string + next: http://api.example.org/accounts/?cursor=cD00ODY%3D" + previous: http://api.example.org/accounts/?cursor=cj0xJnA9NDg3 results: - - id: 57ae8eVBrH7jbDgmYj6Ut2vR9S - organization: example_org - project: example_org - name: example task - status: processing - value: 63 - value_percent: 63 - data: - property1: customValue - created: '2022-08-24T14:15:22Z' - updated: '2022-08-24T16:15:22Z' + - previous: string + next: string + results: + - id: 57ae8eVBrH7jbDgmYj6Ut2vR9S + organization: example_org + project: example_org + name: example task + status: processing + value: 63 + value_percent: 63 + data: + property1: customValue + created: '2022-08-24T14:15:22Z' + updated: '2022-08-24T16:15:22Z' summary: List Response description: '' post: @@ -100,6 +103,9 @@ paths: status: pending data: property1: customValue + tags: + project: demo + user: admin summary: Create Payload required: true security: @@ -601,9 +607,7 @@ components: status: type: string readOnly: true - config: - type: object - additionalProperties: {} + config: {} created: type: string format: date-time @@ -631,21 +635,25 @@ components: type: string minLength: 1 maxLength: 50 - config: - type: object - additionalProperties: {} + config: {} required: - integration - trigger PaginatedTaskList: type: object + required: + - results properties: next: type: string nullable: true + format: uri + example: http://api.example.org/accounts/?cursor=cD00ODY%3D" previous: type: string nullable: true + format: uri + example: http://api.example.org/accounts/?cursor=cj0xJnA9NDg3 results: type: array items: @@ -661,9 +669,7 @@ components: type: string minLength: 1 maxLength: 50 - config: - type: object - additionalProperties: {} + config: {} PatchedTaskRequest: type: object properties: @@ -675,15 +681,6 @@ components: status: allOf: - $ref: '#/components/schemas/StatusEnum' - description: |- - * `pending` - pending - * `pre_processing` - pre_processing - * `processing` - processing - * `post_processing` - post_processing - * `success` - success - * `error` - error - * `cancelled` - cancelled - * `stale` - stale default: pending value: type: integer @@ -699,8 +696,6 @@ components: format: int64 description: Maximum value of the task. Defaults to 100. data: - type: object - additionalProperties: {} nullable: true description: Custom metadata start_time: @@ -728,7 +723,15 @@ components: minimum: 1 nullable: true description: Maximum time to allow between task updates before considering - the task stale. Only applies when task is in a running state. (seconds) + the task stale. (seconds) + tags: + type: object + additionalProperties: + type: string + minLength: 2 + maxLength: 255 + description: Tags for the task represented as a mapping from 'namespace' + to 'value'. StatusEnum: enum: - pending @@ -740,6 +743,15 @@ components: - cancelled - stale type: string + description: |- + * `pending` - pending + * `pre_processing` - pre_processing + * `processing` - processing + * `post_processing` - post_processing + * `success` - success + * `error` - error + * `cancelled` - cancelled + * `stale` - stale Task: type: object properties: @@ -760,15 +772,6 @@ components: status: allOf: - $ref: '#/components/schemas/StatusEnum' - description: |- - * `pending` - pending - * `pre_processing` - pre_processing - * `processing` - processing - * `post_processing` - post_processing - * `success` - success - * `error` - error - * `cancelled` - cancelled - * `stale` - stale default: pending value: type: integer @@ -788,8 +791,6 @@ components: readOnly: true nullable: true data: - type: object - additionalProperties: {} nullable: true description: Custom metadata created: @@ -825,13 +826,21 @@ components: minimum: 1 nullable: true description: Maximum time to allow between task updates before considering - the task stale. Only applies when task is in a running state. (seconds) + the task stale. (seconds) url: type: string readOnly: true public_url: type: string readOnly: true + tags: + type: object + additionalProperties: + type: string + maxLength: 255 + minLength: 2 + description: Tags for the task represented as a mapping from 'namespace' + to 'value'. required: - created - id @@ -853,15 +862,6 @@ components: status: allOf: - $ref: '#/components/schemas/StatusEnum' - description: |- - * `pending` - pending - * `pre_processing` - pre_processing - * `processing` - processing - * `post_processing` - post_processing - * `success` - success - * `error` - error - * `cancelled` - cancelled - * `stale` - stale default: pending value: type: integer @@ -877,8 +877,6 @@ components: format: int64 description: Maximum value of the task. Defaults to 100. data: - type: object - additionalProperties: {} nullable: true description: Custom metadata start_time: @@ -906,7 +904,15 @@ components: minimum: 1 nullable: true description: Maximum time to allow between task updates before considering - the task stale. Only applies when task is in a running state. (seconds) + the task stale. (seconds) + tags: + type: object + additionalProperties: + type: string + minLength: 2 + maxLength: 255 + description: Tags for the task represented as a mapping from 'namespace' + to 'value'. required: - name securitySchemes: diff --git a/taskbadger/cli/basics.py b/taskbadger/cli/basics.py index dfa6e4e..bbec648 100644 --- a/taskbadger/cli/basics.py +++ b/taskbadger/cli/basics.py @@ -11,7 +11,7 @@ configure_api, err_console, get_actions, - get_metadata, + merge_kv_json, ) @@ -69,12 +69,23 @@ def create( show_default=False, help="Metadata to associate with the task. Must be valid JSON.", ), + tag: list[str] = typer.Option( + None, + show_default=False, + help="Metadata 'key=value' pair to associate with the task. Can be specified multiple times.", + ), + tags_json: str = typer.Option( + None, + show_default=False, + help="Tags to associate with the task. Must be valid JSON mapping name -> value.", + ), quiet: bool = typer.Option(False, "--quiet", "-q", help="Minimal output. Only the Task ID."), ): """Create a task.""" configure_api(ctx) actions = get_actions(action_def) - metadata = get_metadata(metadata, metadata_json) + metadata = merge_kv_json(metadata, metadata_json) + tags = merge_kv_json(tag, tags_json) try: task = create_task( @@ -84,6 +95,7 @@ def create( data=metadata, actions=actions, monitor_id=monitor_id, + tags=tags, ) except Exception as e: err_console.print(f"Error creating task: {e}") @@ -119,12 +131,23 @@ def update( show_default=False, help="Metadata to associate with the task. Must be valid JSON.", ), + tag: list[str] = typer.Option( + None, + show_default=False, + help="Metadata 'key=value' pair to associate with the task. Can be specified multiple times.", + ), + tags_json: str = typer.Option( + None, + show_default=False, + help="Tags to associate with the task. Must be valid JSON mapping name -> value.", + ), quiet: bool = typer.Option(False, "--quiet", "-q", help="No output."), ): """Update a task.""" configure_api(ctx) actions = get_actions(action_def) - metadata = get_metadata(metadata, metadata_json) + metadata = merge_kv_json(metadata, metadata_json) + tags = merge_kv_json(tag, tags_json) try: task = update_task( @@ -135,6 +158,7 @@ def update( value_max=value_max, data=metadata, actions=actions, + tags=tags, ) except Exception as e: err_console.print(f"Error creating task: {e}") diff --git a/taskbadger/cli/utils.py b/taskbadger/cli/utils.py index b5c4b8c..b6ebf6b 100644 --- a/taskbadger/cli/utils.py +++ b/taskbadger/cli/utils.py @@ -28,7 +28,7 @@ def get_actions(action_def: tuple[str, str, str]) -> list[Action]: return [] -def get_metadata(metadata_kv: list[str], metadata_json: str) -> dict: +def merge_kv_json(metadata_kv: list[str], metadata_json: str) -> dict: metadata = {} for kv in metadata_kv: k, v = kv.strip().split("=", 1) diff --git a/taskbadger/cli/wrapper.py b/taskbadger/cli/wrapper.py index 3df347c..2a9cf16 100644 --- a/taskbadger/cli/wrapper.py +++ b/taskbadger/cli/wrapper.py @@ -2,7 +2,7 @@ from rich import print from taskbadger import DefaultMergeStrategy, Session, StatusEnum, Task -from taskbadger.cli.utils import configure_api, err_console, get_actions +from taskbadger.cli.utils import configure_api, err_console, get_actions, merge_kv_json from taskbadger.process import ProcessRunner @@ -19,6 +19,11 @@ def run( show_default=False, help="Action definition e.g. 'success,error email to:me@email.com'", ), + tag: list[str] = typer.Option( + None, + show_default=False, + help="Tags: 'name=value' pair to associate with the task. Can be specified multiple times.", + ), capture_output: bool = typer.Option(False, help="Capture stdout and stderr."), ): """Execute a command using the CLI and create a Task to track its outcome. @@ -34,6 +39,7 @@ def run( """ configure_api(ctx) actions = get_actions(action_def) + tags = merge_kv_json(tag, "") stale_timeout = update_frequency * 2 with Session(): try: @@ -43,6 +49,7 @@ def run( stale_timeout=stale_timeout, actions=actions, monitor_id=monitor_id, + tags=tags, ) except Exception as e: err_console.print(f"Error creating task: {e}") diff --git a/taskbadger/config.py b/taskbadger/config.py index 055f490..2c13305 100644 --- a/taskbadger/config.py +++ b/taskbadger/config.py @@ -1,6 +1,6 @@ import dataclasses -import inspect import os +import textwrap from pathlib import Path import tomlkit @@ -18,6 +18,7 @@ class Config: organization_slug: str = None project_slug: str = None host: str = _TB_HOST + tags: dict = None def is_valid(self): return bool(self.token and self.organization_slug and self.project_slug) @@ -51,18 +52,26 @@ def from_dict(config_dict, **overrides) -> "Config": organization_slug=overrides.get("org") or _from_env("ORG", defaults.get("org")), project_slug=overrides.get("project") or _from_env("PROJECT", defaults.get("project")), host=overrides.get("host") or auth.get("host"), + tags=config_dict.get("tags", {}), ) def __str__(self): host = "" - if self.host != _TB_HOST: - host = f"\n Host: {self.host}" - return inspect.cleandoc( - f""" + if self.host and self.host != _TB_HOST: + host = f"Host: {self.host or '-'}\n" + tags = "" + if self.tags: + tags = "Tags:\n " + "\n ".join(f"{k}: {v}" for k, v in self.tags.items()) + return ( + textwrap.dedent( + f""" Organization slug: {self.organization_slug or "-"} Project slug: {self.project_slug or "-"} - Auth token: {self.token or "-"}{host} + Auth token: {self.token or "-"} """ + ) + + host + + tags ) @@ -79,6 +88,11 @@ def write_config(config): ) doc.add("auth", table().add("token", config.token)) + if config.tags: + tags = table() + for key, value in config.tags.items(): + tags.add(key, value) + doc.add("tags", tags) config_path = _get_config_path() if not config_path.parent.exists(): diff --git a/taskbadger/decorators.py b/taskbadger/decorators.py index f1004b5..1f722df 100644 --- a/taskbadger/decorators.py +++ b/taskbadger/decorators.py @@ -14,7 +14,7 @@ def track( name: str = None, monitor_id: str = None, max_runtime: int = None, - **kwargs, + **task_kwargs, ): """ Decorator to track a function as a task. @@ -50,7 +50,7 @@ def _inner(*args, **kwargs): status=StatusEnum.PROCESSING, max_runtime=max_runtime, monitor_id=monitor_id, - **kwargs, + **task_kwargs, ) try: result = func(*args, **kwargs) diff --git a/taskbadger/integrations.py b/taskbadger/integrations.py index 104e622..28e9ece 100644 --- a/taskbadger/integrations.py +++ b/taskbadger/integrations.py @@ -2,7 +2,7 @@ from typing import Any from taskbadger.exceptions import TaskbadgerException -from taskbadger.internal.models import ActionRequest, ActionRequestConfig +from taskbadger.internal.models import ActionRequest def from_config(integration_id: str, config: str): @@ -47,8 +47,8 @@ class EmailIntegration(Integration): to: str # custom type id: str = "email" - def request_config(self) -> ActionRequestConfig: - return ActionRequestConfig.from_dict({"to": self.to}) + def request_config(self) -> dict: + return {"to": self.to} @dataclasses.dataclass @@ -56,8 +56,8 @@ class WebhookIntegration(Integration): type = "webhook" id: str - def request_config(self) -> ActionRequestConfig: - return ActionRequestConfig.from_dict({}) + def request_config(self) -> dict: + return {} ALL = [EmailIntegration, WebhookIntegration] diff --git a/taskbadger/internal/api/action_endpoints/action_cancel.py b/taskbadger/internal/api/action_endpoints/action_cancel.py index ad23edf..6eacc2e 100644 --- a/taskbadger/internal/api/action_endpoints/action_cancel.py +++ b/taskbadger/internal/api/action_endpoints/action_cancel.py @@ -1,5 +1,6 @@ from http import HTTPStatus from typing import Any, Optional, Union +from uuid import UUID import httpx @@ -12,18 +13,18 @@ def _get_kwargs( organization_slug: str, project_slug: str, task_id: str, - id: str, + id: UUID, ) -> dict[str, Any]: - pass - - return { + _kwargs: dict[str, Any] = { "method": "delete", "url": f"/api/{organization_slug}/{project_slug}/tasks/{task_id}/actions/{id}/", } + return _kwargs + def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: - if response.status_code == HTTPStatus.NO_CONTENT: + if response.status_code == 204: return None if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) @@ -44,7 +45,7 @@ def sync_detailed( organization_slug: str, project_slug: str, task_id: str, - id: str, + id: UUID, *, client: AuthenticatedClient, ) -> Response[Any]: @@ -56,11 +57,10 @@ def sync_detailed( organization_slug (str): project_slug (str): task_id (str): - id (str): + id (UUID): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -85,7 +85,7 @@ async def asyncio_detailed( organization_slug: str, project_slug: str, task_id: str, - id: str, + id: UUID, *, client: AuthenticatedClient, ) -> Response[Any]: @@ -97,11 +97,10 @@ async def asyncio_detailed( organization_slug (str): project_slug (str): task_id (str): - id (str): + id (UUID): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/taskbadger/internal/api/action_endpoints/action_create.py b/taskbadger/internal/api/action_endpoints/action_create.py index f97d023..ff183b8 100644 --- a/taskbadger/internal/api/action_endpoints/action_create.py +++ b/taskbadger/internal/api/action_endpoints/action_create.py @@ -15,21 +15,26 @@ def _get_kwargs( project_slug: str, task_id: str, *, - json_body: ActionRequest, + body: ActionRequest, ) -> dict[str, Any]: - pass + headers: dict[str, Any] = {} - json_json_body = json_body.to_dict() - - return { + _kwargs: dict[str, Any] = { "method": "post", "url": f"/api/{organization_slug}/{project_slug}/tasks/{task_id}/actions/", - "json": json_json_body, } + _body = body.to_dict() + + _kwargs["json"] = _body + headers["Content-Type"] = "application/json" + + _kwargs["headers"] = headers + return _kwargs + def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Action]: - if response.status_code == HTTPStatus.CREATED: + if response.status_code == 201: response_201 = Action.from_dict(response.json()) return response_201 @@ -54,7 +59,7 @@ def sync_detailed( task_id: str, *, client: AuthenticatedClient, - json_body: ActionRequest, + body: ActionRequest, ) -> Response[Action]: """Create Action @@ -64,11 +69,10 @@ def sync_detailed( organization_slug (str): project_slug (str): task_id (str): - json_body (ActionRequest): + body (ActionRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -79,7 +83,7 @@ def sync_detailed( organization_slug=organization_slug, project_slug=project_slug, task_id=task_id, - json_body=json_body, + body=body, ) response = client.get_httpx_client().request( @@ -95,7 +99,7 @@ def sync( task_id: str, *, client: AuthenticatedClient, - json_body: ActionRequest, + body: ActionRequest, ) -> Optional[Action]: """Create Action @@ -105,11 +109,10 @@ def sync( organization_slug (str): project_slug (str): task_id (str): - json_body (ActionRequest): + body (ActionRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -121,7 +124,7 @@ def sync( project_slug=project_slug, task_id=task_id, client=client, - json_body=json_body, + body=body, ).parsed @@ -131,7 +134,7 @@ async def asyncio_detailed( task_id: str, *, client: AuthenticatedClient, - json_body: ActionRequest, + body: ActionRequest, ) -> Response[Action]: """Create Action @@ -141,11 +144,10 @@ async def asyncio_detailed( organization_slug (str): project_slug (str): task_id (str): - json_body (ActionRequest): + body (ActionRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -156,7 +158,7 @@ async def asyncio_detailed( organization_slug=organization_slug, project_slug=project_slug, task_id=task_id, - json_body=json_body, + body=body, ) response = await client.get_async_httpx_client().request(**kwargs) @@ -170,7 +172,7 @@ async def asyncio( task_id: str, *, client: AuthenticatedClient, - json_body: ActionRequest, + body: ActionRequest, ) -> Optional[Action]: """Create Action @@ -180,11 +182,10 @@ async def asyncio( organization_slug (str): project_slug (str): task_id (str): - json_body (ActionRequest): + body (ActionRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -197,6 +198,6 @@ async def asyncio( project_slug=project_slug, task_id=task_id, client=client, - json_body=json_body, + body=body, ) ).parsed diff --git a/taskbadger/internal/api/action_endpoints/action_get.py b/taskbadger/internal/api/action_endpoints/action_get.py index 4539b58..d3427a0 100644 --- a/taskbadger/internal/api/action_endpoints/action_get.py +++ b/taskbadger/internal/api/action_endpoints/action_get.py @@ -1,5 +1,6 @@ from http import HTTPStatus from typing import Any, Optional, Union +from uuid import UUID import httpx @@ -13,18 +14,18 @@ def _get_kwargs( organization_slug: str, project_slug: str, task_id: str, - id: str, + id: UUID, ) -> dict[str, Any]: - pass - - return { + _kwargs: dict[str, Any] = { "method": "get", "url": f"/api/{organization_slug}/{project_slug}/tasks/{task_id}/actions/{id}/", } + return _kwargs + def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Action]: - if response.status_code == HTTPStatus.OK: + if response.status_code == 200: response_200 = Action.from_dict(response.json()) return response_200 @@ -47,7 +48,7 @@ def sync_detailed( organization_slug: str, project_slug: str, task_id: str, - id: str, + id: UUID, *, client: AuthenticatedClient, ) -> Response[Action]: @@ -59,11 +60,10 @@ def sync_detailed( organization_slug (str): project_slug (str): task_id (str): - id (str): + id (UUID): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -88,7 +88,7 @@ def sync( organization_slug: str, project_slug: str, task_id: str, - id: str, + id: UUID, *, client: AuthenticatedClient, ) -> Optional[Action]: @@ -100,11 +100,10 @@ def sync( organization_slug (str): project_slug (str): task_id (str): - id (str): + id (UUID): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -124,7 +123,7 @@ async def asyncio_detailed( organization_slug: str, project_slug: str, task_id: str, - id: str, + id: UUID, *, client: AuthenticatedClient, ) -> Response[Action]: @@ -136,11 +135,10 @@ async def asyncio_detailed( organization_slug (str): project_slug (str): task_id (str): - id (str): + id (UUID): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -163,7 +161,7 @@ async def asyncio( organization_slug: str, project_slug: str, task_id: str, - id: str, + id: UUID, *, client: AuthenticatedClient, ) -> Optional[Action]: @@ -175,11 +173,10 @@ async def asyncio( organization_slug (str): project_slug (str): task_id (str): - id (str): + id (UUID): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/taskbadger/internal/api/action_endpoints/action_list.py b/taskbadger/internal/api/action_endpoints/action_list.py index 95b1023..6eeb8f4 100644 --- a/taskbadger/internal/api/action_endpoints/action_list.py +++ b/taskbadger/internal/api/action_endpoints/action_list.py @@ -14,18 +14,18 @@ def _get_kwargs( project_slug: str, task_id: str, ) -> dict[str, Any]: - pass - - return { + _kwargs: dict[str, Any] = { "method": "get", "url": f"/api/{organization_slug}/{project_slug}/tasks/{task_id}/actions/", } + return _kwargs + def _parse_response( *, client: Union[AuthenticatedClient, Client], response: httpx.Response ) -> Optional[list["Action"]]: - if response.status_code == HTTPStatus.OK: + if response.status_code == 200: response_200 = [] _response_200 = response.json() for response_200_item_data in _response_200: @@ -68,12 +68,11 @@ def sync_detailed( task_id (str): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[List['Action']] + Response[list['Action']] """ kwargs = _get_kwargs( @@ -106,12 +105,11 @@ def sync( task_id (str): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - List['Action'] + list['Action'] """ return sync_detailed( @@ -139,12 +137,11 @@ async def asyncio_detailed( task_id (str): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[List['Action']] + Response[list['Action']] """ kwargs = _get_kwargs( @@ -175,12 +172,11 @@ async def asyncio( task_id (str): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - List['Action'] + list['Action'] """ return ( diff --git a/taskbadger/internal/api/action_endpoints/action_partial_update.py b/taskbadger/internal/api/action_endpoints/action_partial_update.py index e9e07bb..53937e3 100644 --- a/taskbadger/internal/api/action_endpoints/action_partial_update.py +++ b/taskbadger/internal/api/action_endpoints/action_partial_update.py @@ -1,5 +1,6 @@ from http import HTTPStatus from typing import Any, Optional, Union +from uuid import UUID import httpx @@ -14,23 +15,28 @@ def _get_kwargs( organization_slug: str, project_slug: str, task_id: str, - id: str, + id: UUID, *, - json_body: PatchedActionRequest, + body: PatchedActionRequest, ) -> dict[str, Any]: - pass + headers: dict[str, Any] = {} - json_json_body = json_body.to_dict() - - return { + _kwargs: dict[str, Any] = { "method": "patch", "url": f"/api/{organization_slug}/{project_slug}/tasks/{task_id}/actions/{id}/", - "json": json_json_body, } + _body = body.to_dict() + + _kwargs["json"] = _body + headers["Content-Type"] = "application/json" + + _kwargs["headers"] = headers + return _kwargs + def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Action]: - if response.status_code == HTTPStatus.OK: + if response.status_code == 200: response_200 = Action.from_dict(response.json()) return response_200 @@ -53,10 +59,10 @@ def sync_detailed( organization_slug: str, project_slug: str, task_id: str, - id: str, + id: UUID, *, client: AuthenticatedClient, - json_body: PatchedActionRequest, + body: PatchedActionRequest, ) -> Response[Action]: """Update Action (partial) @@ -66,12 +72,11 @@ def sync_detailed( organization_slug (str): project_slug (str): task_id (str): - id (str): - json_body (PatchedActionRequest): + id (UUID): + body (PatchedActionRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -83,7 +88,7 @@ def sync_detailed( project_slug=project_slug, task_id=task_id, id=id, - json_body=json_body, + body=body, ) response = client.get_httpx_client().request( @@ -97,10 +102,10 @@ def sync( organization_slug: str, project_slug: str, task_id: str, - id: str, + id: UUID, *, client: AuthenticatedClient, - json_body: PatchedActionRequest, + body: PatchedActionRequest, ) -> Optional[Action]: """Update Action (partial) @@ -110,12 +115,11 @@ def sync( organization_slug (str): project_slug (str): task_id (str): - id (str): - json_body (PatchedActionRequest): + id (UUID): + body (PatchedActionRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -128,7 +132,7 @@ def sync( task_id=task_id, id=id, client=client, - json_body=json_body, + body=body, ).parsed @@ -136,10 +140,10 @@ async def asyncio_detailed( organization_slug: str, project_slug: str, task_id: str, - id: str, + id: UUID, *, client: AuthenticatedClient, - json_body: PatchedActionRequest, + body: PatchedActionRequest, ) -> Response[Action]: """Update Action (partial) @@ -149,12 +153,11 @@ async def asyncio_detailed( organization_slug (str): project_slug (str): task_id (str): - id (str): - json_body (PatchedActionRequest): + id (UUID): + body (PatchedActionRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -166,7 +169,7 @@ async def asyncio_detailed( project_slug=project_slug, task_id=task_id, id=id, - json_body=json_body, + body=body, ) response = await client.get_async_httpx_client().request(**kwargs) @@ -178,10 +181,10 @@ async def asyncio( organization_slug: str, project_slug: str, task_id: str, - id: str, + id: UUID, *, client: AuthenticatedClient, - json_body: PatchedActionRequest, + body: PatchedActionRequest, ) -> Optional[Action]: """Update Action (partial) @@ -191,12 +194,11 @@ async def asyncio( organization_slug (str): project_slug (str): task_id (str): - id (str): - json_body (PatchedActionRequest): + id (UUID): + body (PatchedActionRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -210,6 +212,6 @@ async def asyncio( task_id=task_id, id=id, client=client, - json_body=json_body, + body=body, ) ).parsed diff --git a/taskbadger/internal/api/action_endpoints/action_update.py b/taskbadger/internal/api/action_endpoints/action_update.py index a6365ed..2547583 100644 --- a/taskbadger/internal/api/action_endpoints/action_update.py +++ b/taskbadger/internal/api/action_endpoints/action_update.py @@ -1,5 +1,6 @@ from http import HTTPStatus from typing import Any, Optional, Union +from uuid import UUID import httpx @@ -14,23 +15,28 @@ def _get_kwargs( organization_slug: str, project_slug: str, task_id: str, - id: str, + id: UUID, *, - json_body: ActionRequest, + body: ActionRequest, ) -> dict[str, Any]: - pass + headers: dict[str, Any] = {} - json_json_body = json_body.to_dict() - - return { + _kwargs: dict[str, Any] = { "method": "put", "url": f"/api/{organization_slug}/{project_slug}/tasks/{task_id}/actions/{id}/", - "json": json_json_body, } + _body = body.to_dict() + + _kwargs["json"] = _body + headers["Content-Type"] = "application/json" + + _kwargs["headers"] = headers + return _kwargs + def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Action]: - if response.status_code == HTTPStatus.OK: + if response.status_code == 200: response_200 = Action.from_dict(response.json()) return response_200 @@ -53,10 +59,10 @@ def sync_detailed( organization_slug: str, project_slug: str, task_id: str, - id: str, + id: UUID, *, client: AuthenticatedClient, - json_body: ActionRequest, + body: ActionRequest, ) -> Response[Action]: """Update Action @@ -66,12 +72,11 @@ def sync_detailed( organization_slug (str): project_slug (str): task_id (str): - id (str): - json_body (ActionRequest): + id (UUID): + body (ActionRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -83,7 +88,7 @@ def sync_detailed( project_slug=project_slug, task_id=task_id, id=id, - json_body=json_body, + body=body, ) response = client.get_httpx_client().request( @@ -97,10 +102,10 @@ def sync( organization_slug: str, project_slug: str, task_id: str, - id: str, + id: UUID, *, client: AuthenticatedClient, - json_body: ActionRequest, + body: ActionRequest, ) -> Optional[Action]: """Update Action @@ -110,12 +115,11 @@ def sync( organization_slug (str): project_slug (str): task_id (str): - id (str): - json_body (ActionRequest): + id (UUID): + body (ActionRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -128,7 +132,7 @@ def sync( task_id=task_id, id=id, client=client, - json_body=json_body, + body=body, ).parsed @@ -136,10 +140,10 @@ async def asyncio_detailed( organization_slug: str, project_slug: str, task_id: str, - id: str, + id: UUID, *, client: AuthenticatedClient, - json_body: ActionRequest, + body: ActionRequest, ) -> Response[Action]: """Update Action @@ -149,12 +153,11 @@ async def asyncio_detailed( organization_slug (str): project_slug (str): task_id (str): - id (str): - json_body (ActionRequest): + id (UUID): + body (ActionRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -166,7 +169,7 @@ async def asyncio_detailed( project_slug=project_slug, task_id=task_id, id=id, - json_body=json_body, + body=body, ) response = await client.get_async_httpx_client().request(**kwargs) @@ -178,10 +181,10 @@ async def asyncio( organization_slug: str, project_slug: str, task_id: str, - id: str, + id: UUID, *, client: AuthenticatedClient, - json_body: ActionRequest, + body: ActionRequest, ) -> Optional[Action]: """Update Action @@ -191,12 +194,11 @@ async def asyncio( organization_slug (str): project_slug (str): task_id (str): - id (str): - json_body (ActionRequest): + id (UUID): + body (ActionRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -210,6 +212,6 @@ async def asyncio( task_id=task_id, id=id, client=client, - json_body=json_body, + body=body, ) ).parsed diff --git a/taskbadger/internal/api/task_endpoints/task_cancel.py b/taskbadger/internal/api/task_endpoints/task_cancel.py index f5d3acf..7fa65f3 100644 --- a/taskbadger/internal/api/task_endpoints/task_cancel.py +++ b/taskbadger/internal/api/task_endpoints/task_cancel.py @@ -1,5 +1,6 @@ from http import HTTPStatus from typing import Any, Optional, Union +from uuid import UUID import httpx @@ -11,18 +12,18 @@ def _get_kwargs( organization_slug: str, project_slug: str, - id: str, + id: UUID, ) -> dict[str, Any]: - pass - - return { + _kwargs: dict[str, Any] = { "method": "delete", "url": f"/api/{organization_slug}/{project_slug}/tasks/{id}/", } + return _kwargs + def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]: - if response.status_code == HTTPStatus.NO_CONTENT: + if response.status_code == 204: return None if client.raise_on_unexpected_status: raise errors.UnexpectedStatus(response.status_code, response.content) @@ -42,7 +43,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( organization_slug: str, project_slug: str, - id: str, + id: UUID, *, client: AuthenticatedClient, ) -> Response[Any]: @@ -53,11 +54,10 @@ def sync_detailed( Args: organization_slug (str): project_slug (str): - id (str): + id (UUID): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -80,7 +80,7 @@ def sync_detailed( async def asyncio_detailed( organization_slug: str, project_slug: str, - id: str, + id: UUID, *, client: AuthenticatedClient, ) -> Response[Any]: @@ -91,11 +91,10 @@ async def asyncio_detailed( Args: organization_slug (str): project_slug (str): - id (str): + id (UUID): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/taskbadger/internal/api/task_endpoints/task_create.py b/taskbadger/internal/api/task_endpoints/task_create.py index 22f21c6..2df8ccd 100644 --- a/taskbadger/internal/api/task_endpoints/task_create.py +++ b/taskbadger/internal/api/task_endpoints/task_create.py @@ -1,5 +1,6 @@ from http import HTTPStatus from typing import Any, Optional, Union +from uuid import UUID import httpx @@ -14,25 +15,29 @@ def _get_kwargs( organization_slug: str, project_slug: str, *, - json_body: TaskRequest, - x_taskbadger_monitor: Union[Unset, str] = UNSET, + body: TaskRequest, + x_taskbadger_monitor: Union[Unset, UUID] = UNSET, ) -> dict[str, Any]: - headers = {} + headers: dict[str, Any] = {} if not isinstance(x_taskbadger_monitor, Unset): headers["X-TASKBADGER-MONITOR"] = x_taskbadger_monitor - json_json_body = json_body.to_dict() - - return { + _kwargs: dict[str, Any] = { "method": "post", "url": f"/api/{organization_slug}/{project_slug}/tasks/", - "json": json_json_body, - "headers": headers, } + _body = body.to_dict() + + _kwargs["json"] = _body + headers["Content-Type"] = "application/json" + + _kwargs["headers"] = headers + return _kwargs + def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Task]: - if response.status_code == HTTPStatus.CREATED: + if response.status_code == 201: response_201 = Task.from_dict(response.json()) return response_201 @@ -56,8 +61,8 @@ def sync_detailed( project_slug: str, *, client: AuthenticatedClient, - json_body: TaskRequest, - x_taskbadger_monitor: Union[Unset, str] = UNSET, + body: TaskRequest, + x_taskbadger_monitor: Union[Unset, UUID] = UNSET, ) -> Response[Task]: """Create Task @@ -66,12 +71,11 @@ def sync_detailed( Args: organization_slug (str): project_slug (str): - x_taskbadger_monitor (Union[Unset, str]): - json_body (TaskRequest): + x_taskbadger_monitor (Union[Unset, UUID]): + body (TaskRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -81,7 +85,7 @@ def sync_detailed( kwargs = _get_kwargs( organization_slug=organization_slug, project_slug=project_slug, - json_body=json_body, + body=body, x_taskbadger_monitor=x_taskbadger_monitor, ) @@ -97,8 +101,8 @@ def sync( project_slug: str, *, client: AuthenticatedClient, - json_body: TaskRequest, - x_taskbadger_monitor: Union[Unset, str] = UNSET, + body: TaskRequest, + x_taskbadger_monitor: Union[Unset, UUID] = UNSET, ) -> Optional[Task]: """Create Task @@ -107,12 +111,11 @@ def sync( Args: organization_slug (str): project_slug (str): - x_taskbadger_monitor (Union[Unset, str]): - json_body (TaskRequest): + x_taskbadger_monitor (Union[Unset, UUID]): + body (TaskRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -123,7 +126,7 @@ def sync( organization_slug=organization_slug, project_slug=project_slug, client=client, - json_body=json_body, + body=body, x_taskbadger_monitor=x_taskbadger_monitor, ).parsed @@ -133,8 +136,8 @@ async def asyncio_detailed( project_slug: str, *, client: AuthenticatedClient, - json_body: TaskRequest, - x_taskbadger_monitor: Union[Unset, str] = UNSET, + body: TaskRequest, + x_taskbadger_monitor: Union[Unset, UUID] = UNSET, ) -> Response[Task]: """Create Task @@ -143,12 +146,11 @@ async def asyncio_detailed( Args: organization_slug (str): project_slug (str): - x_taskbadger_monitor (Union[Unset, str]): - json_body (TaskRequest): + x_taskbadger_monitor (Union[Unset, UUID]): + body (TaskRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -158,7 +160,7 @@ async def asyncio_detailed( kwargs = _get_kwargs( organization_slug=organization_slug, project_slug=project_slug, - json_body=json_body, + body=body, x_taskbadger_monitor=x_taskbadger_monitor, ) @@ -172,8 +174,8 @@ async def asyncio( project_slug: str, *, client: AuthenticatedClient, - json_body: TaskRequest, - x_taskbadger_monitor: Union[Unset, str] = UNSET, + body: TaskRequest, + x_taskbadger_monitor: Union[Unset, UUID] = UNSET, ) -> Optional[Task]: """Create Task @@ -182,12 +184,11 @@ async def asyncio( Args: organization_slug (str): project_slug (str): - x_taskbadger_monitor (Union[Unset, str]): - json_body (TaskRequest): + x_taskbadger_monitor (Union[Unset, UUID]): + body (TaskRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -199,7 +200,7 @@ async def asyncio( organization_slug=organization_slug, project_slug=project_slug, client=client, - json_body=json_body, + body=body, x_taskbadger_monitor=x_taskbadger_monitor, ) ).parsed diff --git a/taskbadger/internal/api/task_endpoints/task_get.py b/taskbadger/internal/api/task_endpoints/task_get.py index bc8ff37..447fe4d 100644 --- a/taskbadger/internal/api/task_endpoints/task_get.py +++ b/taskbadger/internal/api/task_endpoints/task_get.py @@ -1,5 +1,6 @@ from http import HTTPStatus from typing import Any, Optional, Union +from uuid import UUID import httpx @@ -12,18 +13,18 @@ def _get_kwargs( organization_slug: str, project_slug: str, - id: str, + id: UUID, ) -> dict[str, Any]: - pass - - return { + _kwargs: dict[str, Any] = { "method": "get", "url": f"/api/{organization_slug}/{project_slug}/tasks/{id}/", } + return _kwargs + def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Task]: - if response.status_code == HTTPStatus.OK: + if response.status_code == 200: response_200 = Task.from_dict(response.json()) return response_200 @@ -45,7 +46,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( organization_slug: str, project_slug: str, - id: str, + id: UUID, *, client: AuthenticatedClient, ) -> Response[Task]: @@ -56,11 +57,10 @@ def sync_detailed( Args: organization_slug (str): project_slug (str): - id (str): + id (UUID): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -83,7 +83,7 @@ def sync_detailed( def sync( organization_slug: str, project_slug: str, - id: str, + id: UUID, *, client: AuthenticatedClient, ) -> Optional[Task]: @@ -94,11 +94,10 @@ def sync( Args: organization_slug (str): project_slug (str): - id (str): + id (UUID): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -116,7 +115,7 @@ def sync( async def asyncio_detailed( organization_slug: str, project_slug: str, - id: str, + id: UUID, *, client: AuthenticatedClient, ) -> Response[Task]: @@ -127,11 +126,10 @@ async def asyncio_detailed( Args: organization_slug (str): project_slug (str): - id (str): + id (UUID): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -152,7 +150,7 @@ async def asyncio_detailed( async def asyncio( organization_slug: str, project_slug: str, - id: str, + id: UUID, *, client: AuthenticatedClient, ) -> Optional[Task]: @@ -163,11 +161,10 @@ async def asyncio( Args: organization_slug (str): project_slug (str): - id (str): + id (UUID): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/taskbadger/internal/api/task_endpoints/task_list.py b/taskbadger/internal/api/task_endpoints/task_list.py index b620784..86a24f1 100644 --- a/taskbadger/internal/api/task_endpoints/task_list.py +++ b/taskbadger/internal/api/task_endpoints/task_list.py @@ -13,29 +13,30 @@ def _get_kwargs( organization_slug: str, project_slug: str, *, - cursor: Union[Unset, None, str] = UNSET, - page_size: Union[Unset, None, int] = UNSET, + cursor: Union[Unset, str] = UNSET, + page_size: Union[Unset, int] = UNSET, ) -> dict[str, Any]: - pass - params: dict[str, Any] = {} + params["cursor"] = cursor params["page_size"] = page_size params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - return { + _kwargs: dict[str, Any] = { "method": "get", "url": f"/api/{organization_slug}/{project_slug}/tasks/", "params": params, } + return _kwargs + def _parse_response( *, client: Union[AuthenticatedClient, Client], response: httpx.Response ) -> Optional[PaginatedTaskList]: - if response.status_code == HTTPStatus.OK: + if response.status_code == 200: response_200 = PaginatedTaskList.from_dict(response.json()) return response_200 @@ -61,8 +62,8 @@ def sync_detailed( project_slug: str, *, client: AuthenticatedClient, - cursor: Union[Unset, None, str] = UNSET, - page_size: Union[Unset, None, int] = UNSET, + cursor: Union[Unset, str] = UNSET, + page_size: Union[Unset, int] = UNSET, ) -> Response[PaginatedTaskList]: """List Tasks @@ -71,12 +72,11 @@ def sync_detailed( Args: organization_slug (str): project_slug (str): - cursor (Union[Unset, None, str]): - page_size (Union[Unset, None, int]): + cursor (Union[Unset, str]): + page_size (Union[Unset, int]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -102,8 +102,8 @@ def sync( project_slug: str, *, client: AuthenticatedClient, - cursor: Union[Unset, None, str] = UNSET, - page_size: Union[Unset, None, int] = UNSET, + cursor: Union[Unset, str] = UNSET, + page_size: Union[Unset, int] = UNSET, ) -> Optional[PaginatedTaskList]: """List Tasks @@ -112,12 +112,11 @@ def sync( Args: organization_slug (str): project_slug (str): - cursor (Union[Unset, None, str]): - page_size (Union[Unset, None, int]): + cursor (Union[Unset, str]): + page_size (Union[Unset, int]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -138,8 +137,8 @@ async def asyncio_detailed( project_slug: str, *, client: AuthenticatedClient, - cursor: Union[Unset, None, str] = UNSET, - page_size: Union[Unset, None, int] = UNSET, + cursor: Union[Unset, str] = UNSET, + page_size: Union[Unset, int] = UNSET, ) -> Response[PaginatedTaskList]: """List Tasks @@ -148,12 +147,11 @@ async def asyncio_detailed( Args: organization_slug (str): project_slug (str): - cursor (Union[Unset, None, str]): - page_size (Union[Unset, None, int]): + cursor (Union[Unset, str]): + page_size (Union[Unset, int]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -177,8 +175,8 @@ async def asyncio( project_slug: str, *, client: AuthenticatedClient, - cursor: Union[Unset, None, str] = UNSET, - page_size: Union[Unset, None, int] = UNSET, + cursor: Union[Unset, str] = UNSET, + page_size: Union[Unset, int] = UNSET, ) -> Optional[PaginatedTaskList]: """List Tasks @@ -187,12 +185,11 @@ async def asyncio( Args: organization_slug (str): project_slug (str): - cursor (Union[Unset, None, str]): - page_size (Union[Unset, None, int]): + cursor (Union[Unset, str]): + page_size (Union[Unset, int]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/taskbadger/internal/api/task_endpoints/task_partial_update.py b/taskbadger/internal/api/task_endpoints/task_partial_update.py index 930b173..384ee78 100644 --- a/taskbadger/internal/api/task_endpoints/task_partial_update.py +++ b/taskbadger/internal/api/task_endpoints/task_partial_update.py @@ -1,5 +1,6 @@ from http import HTTPStatus from typing import Any, Optional, Union +from uuid import UUID import httpx @@ -13,23 +14,28 @@ def _get_kwargs( organization_slug: str, project_slug: str, - id: str, + id: UUID, *, - json_body: PatchedTaskRequest, + body: PatchedTaskRequest, ) -> dict[str, Any]: - pass + headers: dict[str, Any] = {} - json_json_body = json_body.to_dict() - - return { + _kwargs: dict[str, Any] = { "method": "patch", "url": f"/api/{organization_slug}/{project_slug}/tasks/{id}/", - "json": json_json_body, } + _body = body.to_dict() + + _kwargs["json"] = _body + headers["Content-Type"] = "application/json" + + _kwargs["headers"] = headers + return _kwargs + def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Task]: - if response.status_code == HTTPStatus.OK: + if response.status_code == 200: response_200 = Task.from_dict(response.json()) return response_200 @@ -51,10 +57,10 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( organization_slug: str, project_slug: str, - id: str, + id: UUID, *, client: AuthenticatedClient, - json_body: PatchedTaskRequest, + body: PatchedTaskRequest, ) -> Response[Task]: """Update Task (partial) @@ -63,12 +69,11 @@ def sync_detailed( Args: organization_slug (str): project_slug (str): - id (str): - json_body (PatchedTaskRequest): + id (UUID): + body (PatchedTaskRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -79,7 +84,7 @@ def sync_detailed( organization_slug=organization_slug, project_slug=project_slug, id=id, - json_body=json_body, + body=body, ) response = client.get_httpx_client().request( @@ -92,10 +97,10 @@ def sync_detailed( def sync( organization_slug: str, project_slug: str, - id: str, + id: UUID, *, client: AuthenticatedClient, - json_body: PatchedTaskRequest, + body: PatchedTaskRequest, ) -> Optional[Task]: """Update Task (partial) @@ -104,12 +109,11 @@ def sync( Args: organization_slug (str): project_slug (str): - id (str): - json_body (PatchedTaskRequest): + id (UUID): + body (PatchedTaskRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -121,17 +125,17 @@ def sync( project_slug=project_slug, id=id, client=client, - json_body=json_body, + body=body, ).parsed async def asyncio_detailed( organization_slug: str, project_slug: str, - id: str, + id: UUID, *, client: AuthenticatedClient, - json_body: PatchedTaskRequest, + body: PatchedTaskRequest, ) -> Response[Task]: """Update Task (partial) @@ -140,12 +144,11 @@ async def asyncio_detailed( Args: organization_slug (str): project_slug (str): - id (str): - json_body (PatchedTaskRequest): + id (UUID): + body (PatchedTaskRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -156,7 +159,7 @@ async def asyncio_detailed( organization_slug=organization_slug, project_slug=project_slug, id=id, - json_body=json_body, + body=body, ) response = await client.get_async_httpx_client().request(**kwargs) @@ -167,10 +170,10 @@ async def asyncio_detailed( async def asyncio( organization_slug: str, project_slug: str, - id: str, + id: UUID, *, client: AuthenticatedClient, - json_body: PatchedTaskRequest, + body: PatchedTaskRequest, ) -> Optional[Task]: """Update Task (partial) @@ -179,12 +182,11 @@ async def asyncio( Args: organization_slug (str): project_slug (str): - id (str): - json_body (PatchedTaskRequest): + id (UUID): + body (PatchedTaskRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -197,6 +199,6 @@ async def asyncio( project_slug=project_slug, id=id, client=client, - json_body=json_body, + body=body, ) ).parsed diff --git a/taskbadger/internal/api/task_endpoints/task_update.py b/taskbadger/internal/api/task_endpoints/task_update.py index 977f62e..b21e63b 100644 --- a/taskbadger/internal/api/task_endpoints/task_update.py +++ b/taskbadger/internal/api/task_endpoints/task_update.py @@ -1,5 +1,6 @@ from http import HTTPStatus from typing import Any, Optional, Union +from uuid import UUID import httpx @@ -13,23 +14,28 @@ def _get_kwargs( organization_slug: str, project_slug: str, - id: str, + id: UUID, *, - json_body: TaskRequest, + body: TaskRequest, ) -> dict[str, Any]: - pass + headers: dict[str, Any] = {} - json_json_body = json_body.to_dict() - - return { + _kwargs: dict[str, Any] = { "method": "put", "url": f"/api/{organization_slug}/{project_slug}/tasks/{id}/", - "json": json_json_body, } + _body = body.to_dict() + + _kwargs["json"] = _body + headers["Content-Type"] = "application/json" + + _kwargs["headers"] = headers + return _kwargs + def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Task]: - if response.status_code == HTTPStatus.OK: + if response.status_code == 200: response_200 = Task.from_dict(response.json()) return response_200 @@ -51,10 +57,10 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt def sync_detailed( organization_slug: str, project_slug: str, - id: str, + id: UUID, *, client: AuthenticatedClient, - json_body: TaskRequest, + body: TaskRequest, ) -> Response[Task]: """Update Task @@ -63,12 +69,11 @@ def sync_detailed( Args: organization_slug (str): project_slug (str): - id (str): - json_body (TaskRequest): + id (UUID): + body (TaskRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -79,7 +84,7 @@ def sync_detailed( organization_slug=organization_slug, project_slug=project_slug, id=id, - json_body=json_body, + body=body, ) response = client.get_httpx_client().request( @@ -92,10 +97,10 @@ def sync_detailed( def sync( organization_slug: str, project_slug: str, - id: str, + id: UUID, *, client: AuthenticatedClient, - json_body: TaskRequest, + body: TaskRequest, ) -> Optional[Task]: """Update Task @@ -104,12 +109,11 @@ def sync( Args: organization_slug (str): project_slug (str): - id (str): - json_body (TaskRequest): + id (UUID): + body (TaskRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -121,17 +125,17 @@ def sync( project_slug=project_slug, id=id, client=client, - json_body=json_body, + body=body, ).parsed async def asyncio_detailed( organization_slug: str, project_slug: str, - id: str, + id: UUID, *, client: AuthenticatedClient, - json_body: TaskRequest, + body: TaskRequest, ) -> Response[Task]: """Update Task @@ -140,12 +144,11 @@ async def asyncio_detailed( Args: organization_slug (str): project_slug (str): - id (str): - json_body (TaskRequest): + id (UUID): + body (TaskRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -156,7 +159,7 @@ async def asyncio_detailed( organization_slug=organization_slug, project_slug=project_slug, id=id, - json_body=json_body, + body=body, ) response = await client.get_async_httpx_client().request(**kwargs) @@ -167,10 +170,10 @@ async def asyncio_detailed( async def asyncio( organization_slug: str, project_slug: str, - id: str, + id: UUID, *, client: AuthenticatedClient, - json_body: TaskRequest, + body: TaskRequest, ) -> Optional[Task]: """Update Task @@ -179,12 +182,11 @@ async def asyncio( Args: organization_slug (str): project_slug (str): - id (str): - json_body (TaskRequest): + id (UUID): + body (TaskRequest): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code - and Client.raise_on_unexpected_status is True. + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -197,6 +199,6 @@ async def asyncio( project_slug=project_slug, id=id, client=client, - json_body=json_body, + body=body, ) ).parsed diff --git a/taskbadger/internal/client.py b/taskbadger/internal/client.py index c9a3607..e80446f 100644 --- a/taskbadger/internal/client.py +++ b/taskbadger/internal/client.py @@ -20,13 +20,12 @@ class Client: ``timeout``: The maximum amount of a time a request can take. API functions will raise httpx.TimeoutException if this is exceeded. - ``verify_ssl``: Whether or not to verify the SSL certificate of the API server. - This should be True in production, but can be set to False for testing purposes. + ``verify_ssl``: Whether or not to verify the SSL certificate of the API server. This should be True in production, + but can be set to False for testing purposes. ``follow_redirects``: Whether or not to follow redirects. Default value is False. - ``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` - and ``httpx.AsyncClient`` constructor. + ``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` and ``httpx.AsyncClient`` constructor. Attributes: @@ -36,13 +35,13 @@ class Client: """ raise_on_unexpected_status: bool = field(default=False, kw_only=True) - _base_url: str - _cookies: dict[str, str] = field(factory=dict, kw_only=True) - _headers: dict[str, str] = field(factory=dict, kw_only=True) - _timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True) - _verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True) - _follow_redirects: bool = field(default=False, kw_only=True) - _httpx_args: dict[str, Any] = field(factory=dict, kw_only=True) + _base_url: str = field(alias="base_url") + _cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") + _headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers") + _timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True, alias="timeout") + _verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True, alias="verify_ssl") + _follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects") + _httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") _client: Optional[httpx.Client] = field(default=None, init=False) _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) @@ -71,7 +70,7 @@ def with_timeout(self, timeout: httpx.Timeout) -> "Client": return evolve(self, timeout=timeout) def set_httpx_client(self, client: httpx.Client) -> "Client": - """Manually the underlying httpx.Client + """Manually set the underlying httpx.Client **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. """ @@ -148,13 +147,12 @@ class AuthenticatedClient: ``timeout``: The maximum amount of a time a request can take. API functions will raise httpx.TimeoutException if this is exceeded. - ``verify_ssl``: Whether or not to verify the SSL certificate of the API server. - This should be True in production, but can be set to False for testing purposes. + ``verify_ssl``: Whether or not to verify the SSL certificate of the API server. This should be True in production, + but can be set to False for testing purposes. ``follow_redirects``: Whether or not to follow redirects. Default value is False. - ``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` - and ``httpx.AsyncClient`` constructor. + ``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` and ``httpx.AsyncClient`` constructor. Attributes: @@ -167,13 +165,13 @@ class AuthenticatedClient: """ raise_on_unexpected_status: bool = field(default=False, kw_only=True) - _base_url: str - _cookies: dict[str, str] = field(factory=dict, kw_only=True) - _headers: dict[str, str] = field(factory=dict, kw_only=True) - _timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True) - _verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True) - _follow_redirects: bool = field(default=False, kw_only=True) - _httpx_args: dict[str, Any] = field(factory=dict, kw_only=True) + _base_url: str = field(alias="base_url") + _cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") + _headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers") + _timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True, alias="timeout") + _verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True, alias="verify_ssl") + _follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects") + _httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") _client: Optional[httpx.Client] = field(default=None, init=False) _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) @@ -206,7 +204,7 @@ def with_timeout(self, timeout: httpx.Timeout) -> "AuthenticatedClient": return evolve(self, timeout=timeout) def set_httpx_client(self, client: httpx.Client) -> "AuthenticatedClient": - """Manually the underlying httpx.Client + """Manually set the underlying httpx.Client **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. """ diff --git a/taskbadger/internal/errors.py b/taskbadger/internal/errors.py index a581eaa..5f92e76 100644 --- a/taskbadger/internal/errors.py +++ b/taskbadger/internal/errors.py @@ -2,14 +2,15 @@ class UnexpectedStatus(Exception): - """Raised by api functions when the response status an undocumented status - and Client.raise_on_unexpected_status is True""" + """Raised by api functions when the response status an undocumented status and Client.raise_on_unexpected_status is True""" def __init__(self, status_code: int, content: bytes): self.status_code = status_code self.content = content - super().__init__(f"Unexpected status code: {status_code}") + super().__init__( + f"Unexpected status code: {status_code}\n\nResponse content:\n{content.decode(errors='ignore')}" + ) __all__ = ["UnexpectedStatus"] diff --git a/taskbadger/internal/models/__init__.py b/taskbadger/internal/models/__init__.py index 5f29be1..0372837 100644 --- a/taskbadger/internal/models/__init__.py +++ b/taskbadger/internal/models/__init__.py @@ -1,33 +1,27 @@ """Contains all the data models used in inputs/outputs""" from .action import Action -from .action_config import ActionConfig from .action_request import ActionRequest -from .action_request_config import ActionRequestConfig from .paginated_task_list import PaginatedTaskList from .patched_action_request import PatchedActionRequest -from .patched_action_request_config import PatchedActionRequestConfig from .patched_task_request import PatchedTaskRequest -from .patched_task_request_data import PatchedTaskRequestData +from .patched_task_request_tags import PatchedTaskRequestTags from .status_enum import StatusEnum from .task import Task -from .task_data import TaskData from .task_request import TaskRequest -from .task_request_data import TaskRequestData +from .task_request_tags import TaskRequestTags +from .task_tags import TaskTags __all__ = ( "Action", - "ActionConfig", "ActionRequest", - "ActionRequestConfig", "PaginatedTaskList", "PatchedActionRequest", - "PatchedActionRequestConfig", "PatchedTaskRequest", - "PatchedTaskRequestData", + "PatchedTaskRequestTags", "StatusEnum", "Task", - "TaskData", "TaskRequest", - "TaskRequestData", + "TaskRequestTags", + "TaskTags", ) diff --git a/taskbadger/internal/models/action.py b/taskbadger/internal/models/action.py index 9ac0431..61d1e51 100644 --- a/taskbadger/internal/models/action.py +++ b/taskbadger/internal/models/action.py @@ -1,5 +1,5 @@ import datetime -from typing import TYPE_CHECKING, Any, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -7,10 +7,6 @@ from ..types import UNSET, Unset -if TYPE_CHECKING: - from ..models.action_config import ActionConfig - - T = TypeVar("T", bound="Action") @@ -25,7 +21,7 @@ class Action: status (str): created (datetime.datetime): updated (datetime.datetime): - config (Union[Unset, ActionConfig]): + config (Union[Unset, Any]): """ id: int @@ -35,22 +31,25 @@ class Action: status: str created: datetime.datetime updated: datetime.datetime - config: Union[Unset, "ActionConfig"] = UNSET + config: Union[Unset, Any] = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: id = self.id + task = self.task + trigger = self.trigger + integration = self.integration + status = self.status + created = self.created.isoformat() updated = self.updated.isoformat() - config: Union[Unset, dict[str, Any]] = UNSET - if not isinstance(self.config, Unset): - config = self.config.to_dict() + config = self.config field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) @@ -72,8 +71,6 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: - from ..models.action_config import ActionConfig - d = src_dict.copy() id = d.pop("id") @@ -89,12 +86,7 @@ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: updated = isoparse(d.pop("updated")) - _config = d.pop("config", UNSET) - config: Union[Unset, ActionConfig] - if isinstance(_config, Unset): - config = UNSET - else: - config = ActionConfig.from_dict(_config) + config = d.pop("config", UNSET) action = cls( id=id, diff --git a/taskbadger/internal/models/action_request.py b/taskbadger/internal/models/action_request.py index 2551c09..8d61f9a 100644 --- a/taskbadger/internal/models/action_request.py +++ b/taskbadger/internal/models/action_request.py @@ -1,14 +1,10 @@ -from typing import TYPE_CHECKING, Any, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field from ..types import UNSET, Unset -if TYPE_CHECKING: - from ..models.action_request_config import ActionRequestConfig - - T = TypeVar("T", bound="ActionRequest") @@ -18,20 +14,20 @@ class ActionRequest: Attributes: trigger (str): integration (str): - config (Union[Unset, ActionRequestConfig]): + config (Union[Unset, Any]): """ trigger: str integration: str - config: Union[Unset, "ActionRequestConfig"] = UNSET + config: Union[Unset, Any] = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: trigger = self.trigger + integration = self.integration - config: Union[Unset, dict[str, Any]] = UNSET - if not isinstance(self.config, Unset): - config = self.config.to_dict() + + config = self.config field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) @@ -48,19 +44,12 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: - from ..models.action_request_config import ActionRequestConfig - d = src_dict.copy() trigger = d.pop("trigger") integration = d.pop("integration") - _config = d.pop("config", UNSET) - config: Union[Unset, ActionRequestConfig] - if isinstance(_config, Unset): - config = UNSET - else: - config = ActionRequestConfig.from_dict(_config) + config = d.pop("config", UNSET) action_request = cls( trigger=trigger, diff --git a/taskbadger/internal/models/paginated_task_list.py b/taskbadger/internal/models/paginated_task_list.py index 3279162..1ff3900 100644 --- a/taskbadger/internal/models/paginated_task_list.py +++ b/taskbadger/internal/models/paginated_task_list.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar, Union, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -16,36 +16,45 @@ class PaginatedTaskList: """ Attributes: - next_ (Union[Unset, None, str]): - previous (Union[Unset, None, str]): - results (Union[Unset, List['Task']]): + results (list['Task']): + next_ (Union[None, Unset, str]): Example: http://api.example.org/accounts/?cursor=cD00ODY%3D". + previous (Union[None, Unset, str]): Example: http://api.example.org/accounts/?cursor=cj0xJnA9NDg3. """ - next_: Union[Unset, None, str] = UNSET - previous: Union[Unset, None, str] = UNSET - results: Union[Unset, list["Task"]] = UNSET + results: list["Task"] + next_: Union[None, Unset, str] = UNSET + previous: Union[None, Unset, str] = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: - next_ = self.next_ - previous = self.previous - results: Union[Unset, list[dict[str, Any]]] = UNSET - if not isinstance(self.results, Unset): - results = [] - for results_item_data in self.results: - results_item = results_item_data.to_dict() + results = [] + for results_item_data in self.results: + results_item = results_item_data.to_dict() + results.append(results_item) + + next_: Union[None, Unset, str] + if isinstance(self.next_, Unset): + next_ = UNSET + else: + next_ = self.next_ - results.append(results_item) + previous: Union[None, Unset, str] + if isinstance(self.previous, Unset): + previous = UNSET + else: + previous = self.previous field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({}) + field_dict.update( + { + "results": results, + } + ) if next_ is not UNSET: field_dict["next"] = next_ if previous is not UNSET: field_dict["previous"] = previous - if results is not UNSET: - field_dict["results"] = results return field_dict @@ -54,21 +63,35 @@ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: from ..models.task import Task d = src_dict.copy() - next_ = d.pop("next", UNSET) - - previous = d.pop("previous", UNSET) - results = [] - _results = d.pop("results", UNSET) - for results_item_data in _results or []: + _results = d.pop("results") + for results_item_data in _results: results_item = Task.from_dict(results_item_data) results.append(results_item) + def _parse_next_(data: object) -> Union[None, Unset, str]: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(Union[None, Unset, str], data) + + next_ = _parse_next_(d.pop("next", UNSET)) + + def _parse_previous(data: object) -> Union[None, Unset, str]: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(Union[None, Unset, str], data) + + previous = _parse_previous(d.pop("previous", UNSET)) + paginated_task_list = cls( + results=results, next_=next_, previous=previous, - results=results, ) paginated_task_list.additional_properties = d diff --git a/taskbadger/internal/models/patched_action_request.py b/taskbadger/internal/models/patched_action_request.py index 524160e..918c97a 100644 --- a/taskbadger/internal/models/patched_action_request.py +++ b/taskbadger/internal/models/patched_action_request.py @@ -1,14 +1,10 @@ -from typing import TYPE_CHECKING, Any, TypeVar, Union +from typing import Any, TypeVar, Union from attrs import define as _attrs_define from attrs import field as _attrs_field from ..types import UNSET, Unset -if TYPE_CHECKING: - from ..models.patched_action_request_config import PatchedActionRequestConfig - - T = TypeVar("T", bound="PatchedActionRequest") @@ -18,20 +14,20 @@ class PatchedActionRequest: Attributes: trigger (Union[Unset, str]): integration (Union[Unset, str]): - config (Union[Unset, PatchedActionRequestConfig]): + config (Union[Unset, Any]): """ trigger: Union[Unset, str] = UNSET integration: Union[Unset, str] = UNSET - config: Union[Unset, "PatchedActionRequestConfig"] = UNSET + config: Union[Unset, Any] = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: trigger = self.trigger + integration = self.integration - config: Union[Unset, dict[str, Any]] = UNSET - if not isinstance(self.config, Unset): - config = self.config.to_dict() + + config = self.config field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) @@ -47,19 +43,12 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: - from ..models.patched_action_request_config import PatchedActionRequestConfig - d = src_dict.copy() trigger = d.pop("trigger", UNSET) integration = d.pop("integration", UNSET) - _config = d.pop("config", UNSET) - config: Union[Unset, PatchedActionRequestConfig] - if isinstance(_config, Unset): - config = UNSET - else: - config = PatchedActionRequestConfig.from_dict(_config) + config = d.pop("config", UNSET) patched_action_request = cls( trigger=trigger, diff --git a/taskbadger/internal/models/patched_task_request.py b/taskbadger/internal/models/patched_task_request.py index 5b2e2cb..2bac7e4 100644 --- a/taskbadger/internal/models/patched_task_request.py +++ b/taskbadger/internal/models/patched_task_request.py @@ -1,5 +1,5 @@ import datetime -from typing import TYPE_CHECKING, Any, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar, Union, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -9,7 +9,7 @@ from ..types import UNSET, Unset if TYPE_CHECKING: - from ..models.patched_task_request_data import PatchedTaskRequestData + from ..models.patched_task_request_tags import PatchedTaskRequestTags T = TypeVar("T", bound="PatchedTaskRequest") @@ -20,53 +20,89 @@ class PatchedTaskRequest: """ Attributes: name (Union[Unset, str]): Name of the task - status (Union[Unset, StatusEnum]): Default: StatusEnum.PENDING. - value (Union[Unset, None, int]): Current progress value. + status (Union[Unset, StatusEnum]): * `pending` - pending + * `pre_processing` - pre_processing + * `processing` - processing + * `post_processing` - post_processing + * `success` - success + * `error` - error + * `cancelled` - cancelled + * `stale` - stale Default: StatusEnum.PENDING. + value (Union[None, Unset, int]): Current progress value. value_max (Union[Unset, int]): Maximum value of the task. Defaults to 100. - data (Union[Unset, None, PatchedTaskRequestData]): Custom metadata - start_time (Union[Unset, None, datetime.datetime]): Datetime when the status is set to a running state. Can be + data (Union[Unset, Any]): Custom metadata + start_time (Union[None, Unset, datetime.datetime]): Datetime when the status is set to a running state. Can be set via the API. - end_time (Union[Unset, None, datetime.datetime]): Datetime when status is set to a terminal value.Can be set via + end_time (Union[None, Unset, datetime.datetime]): Datetime when status is set to a terminal value.Can be set via the API. - max_runtime (Union[Unset, None, int]): Maximum duration the task can be running for before being considered + max_runtime (Union[None, Unset, int]): Maximum duration the task can be running for before being considered failed. (seconds) - stale_timeout (Union[Unset, None, int]): Maximum time to allow between task updates before considering the task - stale. Only applies when task is in a running state. (seconds) + stale_timeout (Union[None, Unset, int]): Maximum time to allow between task updates before considering the task + stale. (seconds) + tags (Union[Unset, PatchedTaskRequestTags]): Tags for the task represented as a mapping from 'namespace' to + 'value'. """ name: Union[Unset, str] = UNSET status: Union[Unset, StatusEnum] = StatusEnum.PENDING - value: Union[Unset, None, int] = UNSET + value: Union[None, Unset, int] = UNSET value_max: Union[Unset, int] = UNSET - data: Union[Unset, None, "PatchedTaskRequestData"] = UNSET - start_time: Union[Unset, None, datetime.datetime] = UNSET - end_time: Union[Unset, None, datetime.datetime] = UNSET - max_runtime: Union[Unset, None, int] = UNSET - stale_timeout: Union[Unset, None, int] = UNSET + data: Union[Unset, Any] = UNSET + start_time: Union[None, Unset, datetime.datetime] = UNSET + end_time: Union[None, Unset, datetime.datetime] = UNSET + max_runtime: Union[None, Unset, int] = UNSET + stale_timeout: Union[None, Unset, int] = UNSET + tags: Union[Unset, "PatchedTaskRequestTags"] = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: name = self.name + status: Union[Unset, str] = UNSET if not isinstance(self.status, Unset): status = self.status.value - value = self.value + value: Union[None, Unset, int] + if isinstance(self.value, Unset): + value = UNSET + else: + value = self.value + value_max = self.value_max - data: Union[Unset, None, dict[str, Any]] = UNSET - if not isinstance(self.data, Unset): - data = self.data.to_dict() if self.data else None - start_time: Union[Unset, None, str] = UNSET - if not isinstance(self.start_time, Unset): - start_time = self.start_time.isoformat() if self.start_time else None + data = self.data - end_time: Union[Unset, None, str] = UNSET - if not isinstance(self.end_time, Unset): - end_time = self.end_time.isoformat() if self.end_time else None + start_time: Union[None, Unset, str] + if isinstance(self.start_time, Unset): + start_time = UNSET + elif isinstance(self.start_time, datetime.datetime): + start_time = self.start_time.isoformat() + else: + start_time = self.start_time - max_runtime = self.max_runtime - stale_timeout = self.stale_timeout + end_time: Union[None, Unset, str] + if isinstance(self.end_time, Unset): + end_time = UNSET + elif isinstance(self.end_time, datetime.datetime): + end_time = self.end_time.isoformat() + else: + end_time = self.end_time + + max_runtime: Union[None, Unset, int] + if isinstance(self.max_runtime, Unset): + max_runtime = UNSET + else: + max_runtime = self.max_runtime + + stale_timeout: Union[None, Unset, int] + if isinstance(self.stale_timeout, Unset): + stale_timeout = UNSET + else: + stale_timeout = self.stale_timeout + + tags: Union[Unset, dict[str, Any]] = UNSET + if not isinstance(self.tags, Unset): + tags = self.tags.to_dict() field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) @@ -89,12 +125,14 @@ def to_dict(self) -> dict[str, Any]: field_dict["max_runtime"] = max_runtime if stale_timeout is not UNSET: field_dict["stale_timeout"] = stale_timeout + if tags is not UNSET: + field_dict["tags"] = tags return field_dict @classmethod def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: - from ..models.patched_task_request_data import PatchedTaskRequestData + from ..models.patched_task_request_tags import PatchedTaskRequestTags d = src_dict.copy() name = d.pop("name", UNSET) @@ -106,40 +144,77 @@ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: else: status = StatusEnum(_status) - value = d.pop("value", UNSET) + def _parse_value(data: object) -> Union[None, Unset, int]: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(Union[None, Unset, int], data) - value_max = d.pop("value_max", UNSET) + value = _parse_value(d.pop("value", UNSET)) - _data = d.pop("data", UNSET) - data: Union[Unset, None, PatchedTaskRequestData] - if _data is None: - data = None - elif isinstance(_data, Unset): - data = UNSET - else: - data = PatchedTaskRequestData.from_dict(_data) - - _start_time = d.pop("start_time", UNSET) - start_time: Union[Unset, None, datetime.datetime] - if _start_time is None: - start_time = None - elif isinstance(_start_time, Unset): - start_time = UNSET - else: - start_time = isoparse(_start_time) + value_max = d.pop("value_max", UNSET) - _end_time = d.pop("end_time", UNSET) - end_time: Union[Unset, None, datetime.datetime] - if _end_time is None: - end_time = None - elif isinstance(_end_time, Unset): - end_time = UNSET + data = d.pop("data", UNSET) + + def _parse_start_time(data: object) -> Union[None, Unset, datetime.datetime]: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, str): + raise TypeError() + start_time_type_0 = isoparse(data) + + return start_time_type_0 + except: # noqa: E722 + pass + return cast(Union[None, Unset, datetime.datetime], data) + + start_time = _parse_start_time(d.pop("start_time", UNSET)) + + def _parse_end_time(data: object) -> Union[None, Unset, datetime.datetime]: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, str): + raise TypeError() + end_time_type_0 = isoparse(data) + + return end_time_type_0 + except: # noqa: E722 + pass + return cast(Union[None, Unset, datetime.datetime], data) + + end_time = _parse_end_time(d.pop("end_time", UNSET)) + + def _parse_max_runtime(data: object) -> Union[None, Unset, int]: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(Union[None, Unset, int], data) + + max_runtime = _parse_max_runtime(d.pop("max_runtime", UNSET)) + + def _parse_stale_timeout(data: object) -> Union[None, Unset, int]: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(Union[None, Unset, int], data) + + stale_timeout = _parse_stale_timeout(d.pop("stale_timeout", UNSET)) + + _tags = d.pop("tags", UNSET) + tags: Union[Unset, PatchedTaskRequestTags] + if isinstance(_tags, Unset): + tags = UNSET else: - end_time = isoparse(_end_time) - - max_runtime = d.pop("max_runtime", UNSET) - - stale_timeout = d.pop("stale_timeout", UNSET) + tags = PatchedTaskRequestTags.from_dict(_tags) patched_task_request = cls( name=name, @@ -151,6 +226,7 @@ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: end_time=end_time, max_runtime=max_runtime, stale_timeout=stale_timeout, + tags=tags, ) patched_task_request.additional_properties = d diff --git a/taskbadger/internal/models/patched_task_request_data.py b/taskbadger/internal/models/patched_task_request_data.py deleted file mode 100644 index 37a9e6b..0000000 --- a/taskbadger/internal/models/patched_task_request_data.py +++ /dev/null @@ -1,44 +0,0 @@ -from typing import Any, TypeVar - -from attrs import define as _attrs_define -from attrs import field as _attrs_field - -T = TypeVar("T", bound="PatchedTaskRequestData") - - -@_attrs_define -class PatchedTaskRequestData: - """Custom metadata""" - - additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - - def to_dict(self) -> dict[str, Any]: - field_dict: dict[str, Any] = {} - field_dict.update(self.additional_properties) - field_dict.update({}) - - return field_dict - - @classmethod - def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: - d = src_dict.copy() - patched_task_request_data = cls() - - patched_task_request_data.additional_properties = d - return patched_task_request_data - - @property - def additional_keys(self) -> list[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/taskbadger/internal/models/patched_action_request_config.py b/taskbadger/internal/models/patched_task_request_tags.py similarity index 63% rename from taskbadger/internal/models/patched_action_request_config.py rename to taskbadger/internal/models/patched_task_request_tags.py index 8fd3b40..2f2886c 100644 --- a/taskbadger/internal/models/patched_action_request_config.py +++ b/taskbadger/internal/models/patched_task_request_tags.py @@ -3,38 +3,37 @@ from attrs import define as _attrs_define from attrs import field as _attrs_field -T = TypeVar("T", bound="PatchedActionRequestConfig") +T = TypeVar("T", bound="PatchedTaskRequestTags") @_attrs_define -class PatchedActionRequestConfig: - """ """ +class PatchedTaskRequestTags: + """Tags for the task represented as a mapping from 'namespace' to 'value'.""" - additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, str] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({}) return field_dict @classmethod def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() - patched_action_request_config = cls() + patched_task_request_tags = cls() - patched_action_request_config.additional_properties = d - return patched_action_request_config + patched_task_request_tags.additional_properties = d + return patched_task_request_tags @property def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) - def __getitem__(self, key: str) -> Any: + def __getitem__(self, key: str) -> str: return self.additional_properties[key] - def __setitem__(self, key: str, value: Any) -> None: + def __setitem__(self, key: str, value: str) -> None: self.additional_properties[key] = value def __delitem__(self, key: str) -> None: diff --git a/taskbadger/internal/models/task.py b/taskbadger/internal/models/task.py index ad68aa0..ce01052 100644 --- a/taskbadger/internal/models/task.py +++ b/taskbadger/internal/models/task.py @@ -1,5 +1,5 @@ import datetime -from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar, Union, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -9,7 +9,7 @@ from ..types import UNSET, Unset if TYPE_CHECKING: - from ..models.task_data import TaskData + from ..models.task_tags import TaskTags T = TypeVar("T", bound="Task") @@ -23,76 +23,118 @@ class Task: organization (str): project (str): name (str): Name of the task + value_percent (Union[None, int]): created (datetime.datetime): updated (datetime.datetime): url (str): public_url (str): - status (Union[Unset, StatusEnum]): Default: StatusEnum.PENDING. - value (Union[Unset, None, int]): Current progress value. + status (Union[Unset, StatusEnum]): * `pending` - pending + * `pre_processing` - pre_processing + * `processing` - processing + * `post_processing` - post_processing + * `success` - success + * `error` - error + * `cancelled` - cancelled + * `stale` - stale Default: StatusEnum.PENDING. + value (Union[None, Unset, int]): Current progress value. value_max (Union[Unset, int]): Maximum value of the task. Defaults to 100. - value_percent (Optional[int]): - data (Union[Unset, None, TaskData]): Custom metadata - start_time (Union[Unset, None, datetime.datetime]): Datetime when the status is set to a running state. Can be + data (Union[Unset, Any]): Custom metadata + start_time (Union[None, Unset, datetime.datetime]): Datetime when the status is set to a running state. Can be set via the API. - end_time (Union[Unset, None, datetime.datetime]): Datetime when status is set to a terminal value.Can be set via + end_time (Union[None, Unset, datetime.datetime]): Datetime when status is set to a terminal value.Can be set via the API. - max_runtime (Union[Unset, None, int]): Maximum duration the task can be running for before being considered + max_runtime (Union[None, Unset, int]): Maximum duration the task can be running for before being considered failed. (seconds) - stale_timeout (Union[Unset, None, int]): Maximum time to allow between task updates before considering the task - stale. Only applies when task is in a running state. (seconds) + stale_timeout (Union[None, Unset, int]): Maximum time to allow between task updates before considering the task + stale. (seconds) + tags (Union[Unset, TaskTags]): Tags for the task represented as a mapping from 'namespace' to 'value'. """ id: str organization: str project: str name: str + value_percent: Union[None, int] created: datetime.datetime updated: datetime.datetime url: str public_url: str - value_percent: Optional[int] status: Union[Unset, StatusEnum] = StatusEnum.PENDING - value: Union[Unset, None, int] = UNSET + value: Union[None, Unset, int] = UNSET value_max: Union[Unset, int] = UNSET - data: Union[Unset, None, "TaskData"] = UNSET - start_time: Union[Unset, None, datetime.datetime] = UNSET - end_time: Union[Unset, None, datetime.datetime] = UNSET - max_runtime: Union[Unset, None, int] = UNSET - stale_timeout: Union[Unset, None, int] = UNSET + data: Union[Unset, Any] = UNSET + start_time: Union[None, Unset, datetime.datetime] = UNSET + end_time: Union[None, Unset, datetime.datetime] = UNSET + max_runtime: Union[None, Unset, int] = UNSET + stale_timeout: Union[None, Unset, int] = UNSET + tags: Union[Unset, "TaskTags"] = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: id = self.id + organization = self.organization + project = self.project + name = self.name + + value_percent: Union[None, int] + value_percent = self.value_percent + created = self.created.isoformat() updated = self.updated.isoformat() url = self.url + public_url = self.public_url + status: Union[Unset, str] = UNSET if not isinstance(self.status, Unset): status = self.status.value - value = self.value + value: Union[None, Unset, int] + if isinstance(self.value, Unset): + value = UNSET + else: + value = self.value + value_max = self.value_max - value_percent = self.value_percent - data: Union[Unset, None, dict[str, Any]] = UNSET - if not isinstance(self.data, Unset): - data = self.data.to_dict() if self.data else None - start_time: Union[Unset, None, str] = UNSET - if not isinstance(self.start_time, Unset): - start_time = self.start_time.isoformat() if self.start_time else None + data = self.data + + start_time: Union[None, Unset, str] + if isinstance(self.start_time, Unset): + start_time = UNSET + elif isinstance(self.start_time, datetime.datetime): + start_time = self.start_time.isoformat() + else: + start_time = self.start_time + + end_time: Union[None, Unset, str] + if isinstance(self.end_time, Unset): + end_time = UNSET + elif isinstance(self.end_time, datetime.datetime): + end_time = self.end_time.isoformat() + else: + end_time = self.end_time + + max_runtime: Union[None, Unset, int] + if isinstance(self.max_runtime, Unset): + max_runtime = UNSET + else: + max_runtime = self.max_runtime - end_time: Union[Unset, None, str] = UNSET - if not isinstance(self.end_time, Unset): - end_time = self.end_time.isoformat() if self.end_time else None + stale_timeout: Union[None, Unset, int] + if isinstance(self.stale_timeout, Unset): + stale_timeout = UNSET + else: + stale_timeout = self.stale_timeout - max_runtime = self.max_runtime - stale_timeout = self.stale_timeout + tags: Union[Unset, dict[str, Any]] = UNSET + if not isinstance(self.tags, Unset): + tags = self.tags.to_dict() field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) @@ -102,11 +144,11 @@ def to_dict(self) -> dict[str, Any]: "organization": organization, "project": project, "name": name, + "value_percent": value_percent, "created": created, "updated": updated, "url": url, "public_url": public_url, - "value_percent": value_percent, } ) if status is not UNSET: @@ -125,12 +167,14 @@ def to_dict(self) -> dict[str, Any]: field_dict["max_runtime"] = max_runtime if stale_timeout is not UNSET: field_dict["stale_timeout"] = stale_timeout + if tags is not UNSET: + field_dict["tags"] = tags return field_dict @classmethod def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: - from ..models.task_data import TaskData + from ..models.task_tags import TaskTags d = src_dict.copy() id = d.pop("id") @@ -141,6 +185,13 @@ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: name = d.pop("name") + def _parse_value_percent(data: object) -> Union[None, int]: + if data is None: + return data + return cast(Union[None, int], data) + + value_percent = _parse_value_percent(d.pop("value_percent")) + created = isoparse(d.pop("created")) updated = isoparse(d.pop("updated")) @@ -156,48 +207,84 @@ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: else: status = StatusEnum(_status) - value = d.pop("value", UNSET) - - value_max = d.pop("value_max", UNSET) - - value_percent = d.pop("value_percent") + def _parse_value(data: object) -> Union[None, Unset, int]: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(Union[None, Unset, int], data) - _data = d.pop("data", UNSET) - data: Union[Unset, None, TaskData] - if _data is None: - data = None - elif isinstance(_data, Unset): - data = UNSET - else: - data = TaskData.from_dict(_data) + value = _parse_value(d.pop("value", UNSET)) - _start_time = d.pop("start_time", UNSET) - start_time: Union[Unset, None, datetime.datetime] - if _start_time is None: - start_time = None - elif isinstance(_start_time, Unset): - start_time = UNSET - else: - start_time = isoparse(_start_time) + value_max = d.pop("value_max", UNSET) - _end_time = d.pop("end_time", UNSET) - end_time: Union[Unset, None, datetime.datetime] - if _end_time is None: - end_time = None - elif isinstance(_end_time, Unset): - end_time = UNSET + data = d.pop("data", UNSET) + + def _parse_start_time(data: object) -> Union[None, Unset, datetime.datetime]: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, str): + raise TypeError() + start_time_type_0 = isoparse(data) + + return start_time_type_0 + except: # noqa: E722 + pass + return cast(Union[None, Unset, datetime.datetime], data) + + start_time = _parse_start_time(d.pop("start_time", UNSET)) + + def _parse_end_time(data: object) -> Union[None, Unset, datetime.datetime]: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, str): + raise TypeError() + end_time_type_0 = isoparse(data) + + return end_time_type_0 + except: # noqa: E722 + pass + return cast(Union[None, Unset, datetime.datetime], data) + + end_time = _parse_end_time(d.pop("end_time", UNSET)) + + def _parse_max_runtime(data: object) -> Union[None, Unset, int]: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(Union[None, Unset, int], data) + + max_runtime = _parse_max_runtime(d.pop("max_runtime", UNSET)) + + def _parse_stale_timeout(data: object) -> Union[None, Unset, int]: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(Union[None, Unset, int], data) + + stale_timeout = _parse_stale_timeout(d.pop("stale_timeout", UNSET)) + + _tags = d.pop("tags", UNSET) + tags: Union[Unset, TaskTags] + if isinstance(_tags, Unset): + tags = UNSET else: - end_time = isoparse(_end_time) - - max_runtime = d.pop("max_runtime", UNSET) - - stale_timeout = d.pop("stale_timeout", UNSET) + tags = TaskTags.from_dict(_tags) task = cls( id=id, organization=organization, project=project, name=name, + value_percent=value_percent, created=created, updated=updated, url=url, @@ -205,12 +292,12 @@ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: status=status, value=value, value_max=value_max, - value_percent=value_percent, data=data, start_time=start_time, end_time=end_time, max_runtime=max_runtime, stale_timeout=stale_timeout, + tags=tags, ) task.additional_properties = d diff --git a/taskbadger/internal/models/task_data.py b/taskbadger/internal/models/task_data.py deleted file mode 100644 index 9b82366..0000000 --- a/taskbadger/internal/models/task_data.py +++ /dev/null @@ -1,44 +0,0 @@ -from typing import Any, TypeVar - -from attrs import define as _attrs_define -from attrs import field as _attrs_field - -T = TypeVar("T", bound="TaskData") - - -@_attrs_define -class TaskData: - """Custom metadata""" - - additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - - def to_dict(self) -> dict[str, Any]: - field_dict: dict[str, Any] = {} - field_dict.update(self.additional_properties) - field_dict.update({}) - - return field_dict - - @classmethod - def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: - d = src_dict.copy() - task_data = cls() - - task_data.additional_properties = d - return task_data - - @property - def additional_keys(self) -> list[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/taskbadger/internal/models/task_request.py b/taskbadger/internal/models/task_request.py index bff1c6b..6d0e107 100644 --- a/taskbadger/internal/models/task_request.py +++ b/taskbadger/internal/models/task_request.py @@ -1,5 +1,5 @@ import datetime -from typing import TYPE_CHECKING, Any, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar, Union, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -9,7 +9,7 @@ from ..types import UNSET, Unset if TYPE_CHECKING: - from ..models.task_request_data import TaskRequestData + from ..models.task_request_tags import TaskRequestTags T = TypeVar("T", bound="TaskRequest") @@ -20,53 +20,88 @@ class TaskRequest: """ Attributes: name (str): Name of the task - status (Union[Unset, StatusEnum]): Default: StatusEnum.PENDING. - value (Union[Unset, None, int]): Current progress value. + status (Union[Unset, StatusEnum]): * `pending` - pending + * `pre_processing` - pre_processing + * `processing` - processing + * `post_processing` - post_processing + * `success` - success + * `error` - error + * `cancelled` - cancelled + * `stale` - stale Default: StatusEnum.PENDING. + value (Union[None, Unset, int]): Current progress value. value_max (Union[Unset, int]): Maximum value of the task. Defaults to 100. - data (Union[Unset, None, TaskRequestData]): Custom metadata - start_time (Union[Unset, None, datetime.datetime]): Datetime when the status is set to a running state. Can be + data (Union[Unset, Any]): Custom metadata + start_time (Union[None, Unset, datetime.datetime]): Datetime when the status is set to a running state. Can be set via the API. - end_time (Union[Unset, None, datetime.datetime]): Datetime when status is set to a terminal value.Can be set via + end_time (Union[None, Unset, datetime.datetime]): Datetime when status is set to a terminal value.Can be set via the API. - max_runtime (Union[Unset, None, int]): Maximum duration the task can be running for before being considered + max_runtime (Union[None, Unset, int]): Maximum duration the task can be running for before being considered failed. (seconds) - stale_timeout (Union[Unset, None, int]): Maximum time to allow between task updates before considering the task - stale. Only applies when task is in a running state. (seconds) + stale_timeout (Union[None, Unset, int]): Maximum time to allow between task updates before considering the task + stale. (seconds) + tags (Union[Unset, TaskRequestTags]): Tags for the task represented as a mapping from 'namespace' to 'value'. """ name: str status: Union[Unset, StatusEnum] = StatusEnum.PENDING - value: Union[Unset, None, int] = UNSET + value: Union[None, Unset, int] = UNSET value_max: Union[Unset, int] = UNSET - data: Union[Unset, None, "TaskRequestData"] = UNSET - start_time: Union[Unset, None, datetime.datetime] = UNSET - end_time: Union[Unset, None, datetime.datetime] = UNSET - max_runtime: Union[Unset, None, int] = UNSET - stale_timeout: Union[Unset, None, int] = UNSET + data: Union[Unset, Any] = UNSET + start_time: Union[None, Unset, datetime.datetime] = UNSET + end_time: Union[None, Unset, datetime.datetime] = UNSET + max_runtime: Union[None, Unset, int] = UNSET + stale_timeout: Union[None, Unset, int] = UNSET + tags: Union[Unset, "TaskRequestTags"] = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: name = self.name + status: Union[Unset, str] = UNSET if not isinstance(self.status, Unset): status = self.status.value - value = self.value + value: Union[None, Unset, int] + if isinstance(self.value, Unset): + value = UNSET + else: + value = self.value + value_max = self.value_max - data: Union[Unset, None, dict[str, Any]] = UNSET - if not isinstance(self.data, Unset): - data = self.data.to_dict() if self.data else None - start_time: Union[Unset, None, str] = UNSET - if not isinstance(self.start_time, Unset): - start_time = self.start_time.isoformat() if self.start_time else None + data = self.data - end_time: Union[Unset, None, str] = UNSET - if not isinstance(self.end_time, Unset): - end_time = self.end_time.isoformat() if self.end_time else None + start_time: Union[None, Unset, str] + if isinstance(self.start_time, Unset): + start_time = UNSET + elif isinstance(self.start_time, datetime.datetime): + start_time = self.start_time.isoformat() + else: + start_time = self.start_time - max_runtime = self.max_runtime - stale_timeout = self.stale_timeout + end_time: Union[None, Unset, str] + if isinstance(self.end_time, Unset): + end_time = UNSET + elif isinstance(self.end_time, datetime.datetime): + end_time = self.end_time.isoformat() + else: + end_time = self.end_time + + max_runtime: Union[None, Unset, int] + if isinstance(self.max_runtime, Unset): + max_runtime = UNSET + else: + max_runtime = self.max_runtime + + stale_timeout: Union[None, Unset, int] + if isinstance(self.stale_timeout, Unset): + stale_timeout = UNSET + else: + stale_timeout = self.stale_timeout + + tags: Union[Unset, dict[str, Any]] = UNSET + if not isinstance(self.tags, Unset): + tags = self.tags.to_dict() field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) @@ -91,12 +126,14 @@ def to_dict(self) -> dict[str, Any]: field_dict["max_runtime"] = max_runtime if stale_timeout is not UNSET: field_dict["stale_timeout"] = stale_timeout + if tags is not UNSET: + field_dict["tags"] = tags return field_dict @classmethod def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: - from ..models.task_request_data import TaskRequestData + from ..models.task_request_tags import TaskRequestTags d = src_dict.copy() name = d.pop("name") @@ -108,40 +145,77 @@ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: else: status = StatusEnum(_status) - value = d.pop("value", UNSET) + def _parse_value(data: object) -> Union[None, Unset, int]: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(Union[None, Unset, int], data) - value_max = d.pop("value_max", UNSET) + value = _parse_value(d.pop("value", UNSET)) - _data = d.pop("data", UNSET) - data: Union[Unset, None, TaskRequestData] - if _data is None: - data = None - elif isinstance(_data, Unset): - data = UNSET - else: - data = TaskRequestData.from_dict(_data) - - _start_time = d.pop("start_time", UNSET) - start_time: Union[Unset, None, datetime.datetime] - if _start_time is None: - start_time = None - elif isinstance(_start_time, Unset): - start_time = UNSET - else: - start_time = isoparse(_start_time) + value_max = d.pop("value_max", UNSET) - _end_time = d.pop("end_time", UNSET) - end_time: Union[Unset, None, datetime.datetime] - if _end_time is None: - end_time = None - elif isinstance(_end_time, Unset): - end_time = UNSET + data = d.pop("data", UNSET) + + def _parse_start_time(data: object) -> Union[None, Unset, datetime.datetime]: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, str): + raise TypeError() + start_time_type_0 = isoparse(data) + + return start_time_type_0 + except: # noqa: E722 + pass + return cast(Union[None, Unset, datetime.datetime], data) + + start_time = _parse_start_time(d.pop("start_time", UNSET)) + + def _parse_end_time(data: object) -> Union[None, Unset, datetime.datetime]: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, str): + raise TypeError() + end_time_type_0 = isoparse(data) + + return end_time_type_0 + except: # noqa: E722 + pass + return cast(Union[None, Unset, datetime.datetime], data) + + end_time = _parse_end_time(d.pop("end_time", UNSET)) + + def _parse_max_runtime(data: object) -> Union[None, Unset, int]: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(Union[None, Unset, int], data) + + max_runtime = _parse_max_runtime(d.pop("max_runtime", UNSET)) + + def _parse_stale_timeout(data: object) -> Union[None, Unset, int]: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(Union[None, Unset, int], data) + + stale_timeout = _parse_stale_timeout(d.pop("stale_timeout", UNSET)) + + _tags = d.pop("tags", UNSET) + tags: Union[Unset, TaskRequestTags] + if isinstance(_tags, Unset): + tags = UNSET else: - end_time = isoparse(_end_time) - - max_runtime = d.pop("max_runtime", UNSET) - - stale_timeout = d.pop("stale_timeout", UNSET) + tags = TaskRequestTags.from_dict(_tags) task_request = cls( name=name, @@ -153,6 +227,7 @@ def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: end_time=end_time, max_runtime=max_runtime, stale_timeout=stale_timeout, + tags=tags, ) task_request.additional_properties = d diff --git a/taskbadger/internal/models/task_request_data.py b/taskbadger/internal/models/task_request_data.py deleted file mode 100644 index 0767e32..0000000 --- a/taskbadger/internal/models/task_request_data.py +++ /dev/null @@ -1,44 +0,0 @@ -from typing import Any, TypeVar - -from attrs import define as _attrs_define -from attrs import field as _attrs_field - -T = TypeVar("T", bound="TaskRequestData") - - -@_attrs_define -class TaskRequestData: - """Custom metadata""" - - additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) - - def to_dict(self) -> dict[str, Any]: - field_dict: dict[str, Any] = {} - field_dict.update(self.additional_properties) - field_dict.update({}) - - return field_dict - - @classmethod - def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: - d = src_dict.copy() - task_request_data = cls() - - task_request_data.additional_properties = d - return task_request_data - - @property - def additional_keys(self) -> list[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/taskbadger/internal/models/action_request_config.py b/taskbadger/internal/models/task_request_tags.py similarity index 65% rename from taskbadger/internal/models/action_request_config.py rename to taskbadger/internal/models/task_request_tags.py index a17bee2..50a6248 100644 --- a/taskbadger/internal/models/action_request_config.py +++ b/taskbadger/internal/models/task_request_tags.py @@ -3,38 +3,37 @@ from attrs import define as _attrs_define from attrs import field as _attrs_field -T = TypeVar("T", bound="ActionRequestConfig") +T = TypeVar("T", bound="TaskRequestTags") @_attrs_define -class ActionRequestConfig: - """ """ +class TaskRequestTags: + """Tags for the task represented as a mapping from 'namespace' to 'value'.""" - additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, str] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({}) return field_dict @classmethod def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() - action_request_config = cls() + task_request_tags = cls() - action_request_config.additional_properties = d - return action_request_config + task_request_tags.additional_properties = d + return task_request_tags @property def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) - def __getitem__(self, key: str) -> Any: + def __getitem__(self, key: str) -> str: return self.additional_properties[key] - def __setitem__(self, key: str, value: Any) -> None: + def __setitem__(self, key: str, value: str) -> None: self.additional_properties[key] = value def __delitem__(self, key: str) -> None: diff --git a/taskbadger/internal/models/action_config.py b/taskbadger/internal/models/task_tags.py similarity index 67% rename from taskbadger/internal/models/action_config.py rename to taskbadger/internal/models/task_tags.py index 1c20001..2123a72 100644 --- a/taskbadger/internal/models/action_config.py +++ b/taskbadger/internal/models/task_tags.py @@ -3,38 +3,37 @@ from attrs import define as _attrs_define from attrs import field as _attrs_field -T = TypeVar("T", bound="ActionConfig") +T = TypeVar("T", bound="TaskTags") @_attrs_define -class ActionConfig: - """ """ +class TaskTags: + """Tags for the task represented as a mapping from 'namespace' to 'value'.""" - additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + additional_properties: dict[str, str] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({}) return field_dict @classmethod def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: d = src_dict.copy() - action_config = cls() + task_tags = cls() - action_config.additional_properties = d - return action_config + task_tags.additional_properties = d + return task_tags @property def additional_keys(self) -> list[str]: return list(self.additional_properties.keys()) - def __getitem__(self, key: str) -> Any: + def __getitem__(self, key: str) -> str: return self.additional_properties[key] - def __setitem__(self, key: str, value: Any) -> None: + def __setitem__(self, key: str, value: str) -> None: self.additional_properties[key] = value def __delitem__(self, key: str) -> None: diff --git a/taskbadger/internal/types.py b/taskbadger/internal/types.py index f7d2608..b9ed58b 100644 --- a/taskbadger/internal/types.py +++ b/taskbadger/internal/types.py @@ -43,4 +43,4 @@ class Response(Generic[T]): parsed: Optional[T] -__all__ = ["File", "Response", "FileJsonType"] +__all__ = ["UNSET", "File", "FileJsonType", "Response", "Unset"] diff --git a/taskbadger/mug.py b/taskbadger/mug.py index 061c720..f9b7a75 100644 --- a/taskbadger/mug.py +++ b/taskbadger/mug.py @@ -83,18 +83,23 @@ class Scope: def __init__(self): self.stack = [] self.context = {} + self.tags = {} def __enter__(self): - self.stack.append(self.context) + self.stack.append((self.context, self.tags)) self.context = self.context.copy() + self.tags = self.tags.copy() return self def __exit__(self, *args): - self.context = self.stack.pop() + self.context, self.tags = self.stack.pop() def __setitem__(self, key, value): self.context[key] = value + def tag(self, tags: dict[str, str]): + self.tags.update(tags) + class MugMeta(type): @property @@ -119,8 +124,9 @@ def __init__(self, settings_or_mug=None): self._session = ReentrantSession() self._scope = Scope() - def bind(self, settings): + def bind(self, settings, tags=None): self.settings = settings + self.scope().tags = tags or {} def session(self) -> ReentrantSession: return self._session diff --git a/taskbadger/sdk.py b/taskbadger/sdk.py index d1d994f..885c17c 100644 --- a/taskbadger/sdk.py +++ b/taskbadger/sdk.py @@ -18,10 +18,10 @@ ) from taskbadger.internal.models import ( PatchedTaskRequest, - PatchedTaskRequestData, + PatchedTaskRequestTags, StatusEnum, TaskRequest, - TaskRequestData, + TaskRequestTags, ) from taskbadger.internal.types import UNSET from taskbadger.mug import Badger, Session, Settings @@ -37,12 +37,13 @@ def init( project_slug: str = None, token: str = None, systems: list[System] = None, + tags: dict[str, str] = None, ): """Initialize Task Badger client Call this function once per thread """ - _init(_TB_HOST, organization_slug, project_slug, token, systems) + _init(_TB_HOST, organization_slug, project_slug, token, systems, tags) def _init( @@ -51,6 +52,7 @@ def _init( project_slug: str = None, token: str = None, systems: list[System] = None, + tags: dict[str, str] = None, ): host = host or os.environ.get("TASKBADGER_HOST", "https://taskbadger.net") organization_slug = organization_slug or os.environ.get("TASKBADGER_ORG") @@ -66,7 +68,7 @@ def _init( project_slug, systems={system.identifier: system for system in systems}, ) - Badger.current.bind(settings) + Badger.current.bind(settings, tags) else: raise ConfigurationError( host=host, @@ -97,6 +99,7 @@ def create_task( stale_timeout: int = None, actions: list[Action] = None, monitor_id: str = None, + tags: dict[str, str] = None, ) -> "Task": """Create a Task. @@ -110,6 +113,7 @@ def create_task( stale_timeout: Maximum allowed time between updates (seconds). actions: Task actions. monitor_id: ID of the monitor to associate this task with. + tags: Dictionary of namespace -> value tags. Returns: Task: The created Task object. @@ -128,13 +132,16 @@ def create_task( max_runtime=max_runtime, stale_timeout=stale_timeout, ) - scope_data = Badger.current.scope().context - if scope_data or data: + scope = Badger.current.scope() + if scope.context or data: data = data or {} - task.data = TaskRequestData.from_dict({**scope_data, **data}) + task.data = {**scope.context, **data} if actions: task.additional_properties = {"actions": [a.to_dict() for a in actions]} - kwargs = _make_args(json_body=task) + if scope.tags or tags: + tags = tags or {} + task.tags = TaskRequestTags.from_dict({**scope.tags, **tags}) + kwargs = _make_args(body=task) if monitor_id: kwargs["x_taskbadger_monitor"] = monitor_id with Session() as client: @@ -153,6 +160,7 @@ def update_task( max_runtime: int = None, stale_timeout: int = None, actions: list[Action] = None, + tags: dict[str, str] = None, ) -> "Task": """Update a task. Requires only the task ID and fields to update. @@ -167,6 +175,7 @@ def update_task( max_runtime: Maximum expected runtime (seconds). stale_timeout: Maximum allowed time between updates (seconds). actions: Task actions. + tags: Dictionary of namespace -> value tags. Returns: Task: The updated Task object. @@ -179,7 +188,7 @@ def update_task( max_runtime = _none_to_unset(max_runtime) stale_timeout = _none_to_unset(stale_timeout) - data = UNSET if not data else PatchedTaskRequestData.from_dict(data) + data = data or UNSET body = PatchedTaskRequest( name=name, status=status, @@ -191,7 +200,9 @@ def update_task( ) if actions: body.additional_properties = {"actions": [a.to_dict() for a in actions]} - kwargs = _make_args(id=task_id, json_body=body) + if tags: + body.tags = PatchedTaskRequestTags.from_dict(tags) + kwargs = _make_args(id=task_id, body=body) with Session() as client: response = task_partial_update.sync_detailed(client=client, **kwargs) _check_response(response) @@ -247,8 +258,12 @@ def create( stale_timeout: int = None, actions: list[Action] = None, monitor_id: str = None, + tags: dict[str, str] = None, ) -> "Task": - """Create a new task""" + """Create a new task + + See [taskbadger.create_task][] for more information. + """ return create_task( name, status, @@ -259,6 +274,7 @@ def create( stale_timeout=stale_timeout, actions=actions, monitor_id=monitor_id, + tags=tags, ) def __init__(self, task): @@ -323,11 +339,14 @@ def update( max_runtime: int = None, stale_timeout: int = None, actions: list[Action] = None, + tags: dict[str, str] = None, data_merge_strategy: Any = None, ): """Generic update method used to update any of the task fields. This can also be used to add actions. + + See [taskbadger.update_task][] for more information. """ if data and data_merge_strategy: if hasattr(data_merge_strategy, "merge"): @@ -347,6 +366,7 @@ def update( max_runtime=max_runtime, stale_timeout=stale_timeout, actions=actions, + tags=tags, ) self._task = task._task @@ -354,14 +374,18 @@ def add_actions(self, actions: list[Action]): """Add actions to the task.""" self.update(actions=actions) + def tag(self, tags: dict[str, str]): + """Add tags to the task.""" + self.update(tags=tags) + def ping(self): """Update the task without changing any values. This can be used in conjunction with 'stale_timeout' to indicate that the task is still running.""" self.update() @property - def data(self): - return self._task.data.additional_properties + def tags(self): + return self._task.tags.to_dict() def __getattr__(self, item): return getattr(self._task, item) diff --git a/tasks.py b/tasks.py index 87f0ae1..a5cc9af 100644 --- a/tasks.py +++ b/tasks.py @@ -31,6 +31,7 @@ def update_api(c, local=False): if not local: c.run("curl http://localhost:8000/api/schema.json > taskbadger.yaml") c.run( - "cd .. && openapi-python-client update --path taskbadger-python/taskbadger.yaml " - "--config taskbadger-python/generator_config.yml" + "openapi-python-client generate --meta=none --path taskbadger.yaml " + "--config generator_config.yml --overwrite " + "--output-path taskbadger/internal" ) diff --git a/tests/conftest.py b/tests/conftest.py index 6abc2fd..5619737 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,8 +3,8 @@ from taskbadger.mug import Badger, Settings -@pytest.fixture -def bind_settings(): +@pytest.fixture() +def _bind_settings(): Badger.current.bind(Settings("https://taskbadger.net", "token", "org", "proj")) yield Badger.current.bind(None) diff --git a/tests/test_celery.py b/tests/test_celery.py index cdea029..39f3cdb 100644 --- a/tests/test_celery.py +++ b/tests/test_celery.py @@ -3,7 +3,7 @@ ==== As part of the Celery fixture setup a 'ping' task is run which executes -before the `bind_settings` fixture is executed. This means that if any code +before the `_bind_settings` fixture is executed. This means that if any code calls `Badger.is_configured()` (or similar), the `_local` ContextVar in the Celery runner thread will not have the configuration set. """ @@ -29,7 +29,8 @@ def _check_log_errors(caplog): pytest.fail(f"log errors during tests: {errors}") -def test_celery_task(celery_session_app, celery_session_worker, bind_settings): +@pytest.mark.usefixtures("_bind_settings") +def test_celery_task(celery_session_app, celery_session_worker): @celery_session_app.task(bind=True, base=Task) def add_normal(self, a, b): assert self.request.get("taskbadger_task") is not None, "missing task in request" @@ -58,7 +59,8 @@ def add_normal(self, a, b): assert Badger.current.session().client is None -def test_celery_task_with_args(celery_session_app, celery_session_worker, bind_settings): +@pytest.mark.usefixtures("_bind_settings") +def test_celery_task_with_args(celery_session_app, celery_session_worker): @celery_session_app.task(bind=True, base=Task) def add_with_task_args(self, a, b): assert self.taskbadger_task is not None @@ -77,14 +79,17 @@ def add_with_task_args(self, a, b): (2, 2), taskbadger_name="new_name", taskbadger_value_max=10, - taskbadger_kwargs={"data": {"foo": "bar"}}, + taskbadger_kwargs={"data": {"foo": "bar"}, "tags": {"bar": "baz"}}, ) assert result.get(timeout=10, propagate=True) == 4 - create.assert_called_once_with("new_name", value_max=10, data={"foo": "bar"}, status=StatusEnum.PENDING) + create.assert_called_once_with( + "new_name", value_max=10, data={"foo": "bar"}, tags={"bar": "baz"}, status=StatusEnum.PENDING + ) -def test_celery_task_with_kwargs(celery_session_app, celery_session_worker, bind_settings): +@pytest.mark.usefixtures("_bind_settings") +def test_celery_task_with_kwargs(celery_session_app, celery_session_worker): @celery_session_app.task(bind=True, base=Task) def add_with_task_args(self, a, b): assert self.taskbadger_task is not None @@ -112,7 +117,8 @@ def add_with_task_args(self, a, b): create.assert_called_once_with("new_name", value_max=10, actions=actions, status=StatusEnum.PENDING) -def test_celery_record_args(celery_session_app, celery_session_worker, bind_settings): +@pytest.mark.usefixtures("_bind_settings") +def test_celery_record_args(celery_session_app, celery_session_worker): @celery_session_app.task(bind=True, base=Task) def add_with_task_args(self, a, b): assert self.taskbadger_task is not None @@ -144,7 +150,8 @@ def add_with_task_args(self, a, b): ) -def test_celery_record_task_kwargs(celery_session_app, celery_session_worker, bind_settings): +@pytest.mark.usefixtures("_bind_settings") +def test_celery_record_task_kwargs(celery_session_app, celery_session_worker): @celery_session_app.task(bind=True, base=Task) def add_with_task_kwargs(self, a, b, c=0): assert self.taskbadger_task is not None @@ -180,7 +187,8 @@ def add_with_task_kwargs(self, a, b, c=0): ) -def test_celery_record_task_args_custom_serialization(celery_session_app, celery_session_worker, bind_settings): +@pytest.mark.usefixtures("_bind_settings") +def test_celery_record_task_args_custom_serialization(celery_session_app, celery_session_worker): class A: def __init__(self, a, b): self.a = a @@ -215,7 +223,8 @@ def add_task_custom_serialization(self, a): ) -def test_celery_task_with_args_in_decorator(celery_session_app, celery_session_worker, bind_settings): +@pytest.mark.usefixtures("_bind_settings") +def test_celery_task_with_args_in_decorator(celery_session_app, celery_session_worker): @celery_session_app.task( bind=True, base=Task, @@ -241,7 +250,8 @@ def add_with_task_args_in_decorator(self, a, b): create.assert_called_once_with(mock.ANY, status=StatusEnum.PENDING, monitor_id="123", value_max=10) -def test_celery_task_retry(celery_session_app, celery_session_worker, bind_settings): +@pytest.mark.usefixtures("_bind_settings") +def test_celery_task_retry(celery_session_app, celery_session_worker): """Note: When a task is retried, the celery task ID remains the same but a new TB task will be created. @@ -328,7 +338,8 @@ def add_direct(self, a, b): assert Badger.current.session().client is None -def test_task_shared_task(celery_session_worker, bind_settings): +@pytest.mark.usefixtures("_bind_settings") +def test_task_shared_task(celery_session_worker): @celery.shared_task(bind=True, base=Task) def add_shared_task(self, a, b): assert self.taskbadger_task is not None @@ -352,7 +363,8 @@ def add_shared_task(self, a, b): assert Badger.current.session().client is None -def test_task_signature(celery_session_worker, bind_settings): +@pytest.mark.usefixtures("_bind_settings") +def test_task_signature(celery_session_worker): @celery.shared_task(bind=True, base=Task) def task_signature(self, a): assert self.taskbadger_task is not None @@ -379,7 +391,8 @@ def task_signature(self, a): assert Badger.current.session().client is None -def test_task_map(celery_session_worker, bind_settings): +@pytest.mark.usefixtures("_bind_settings") +def test_task_map(celery_session_worker): """Tasks executed in a map or starmap are not executed as tasks""" @celery.shared_task(bind=True, base=Task) @@ -406,7 +419,8 @@ def task_map(self, a): assert Badger.current.session().client is None -def test_celery_task_already_in_terminal_state(celery_session_worker, bind_settings): +@pytest.mark.usefixtures("_bind_settings") +def test_celery_task_already_in_terminal_state(celery_session_worker): @celery.shared_task(bind=True, base=Task) def add_manual_update(self, a, b, is_retry=False): # simulate updating the task to a terminal state diff --git a/tests/test_celery_error.py b/tests/test_celery_error.py index 75eea9b..cf14c27 100644 --- a/tests/test_celery_error.py +++ b/tests/test_celery_error.py @@ -8,7 +8,8 @@ from tests.utils import task_for_test -def test_celery_task_error(celery_session_app, celery_session_worker, bind_settings): +@pytest.mark.usefixtures("_bind_settings") +def test_celery_task_error(celery_session_app, celery_session_worker): @celery_session_app.task(bind=True, base=Task) def add_error(self, a, b): assert self.taskbadger_task is not None diff --git a/tests/test_celery_system_integration.py b/tests/test_celery_system_integration.py index cd0829a..24a0fe4 100644 --- a/tests/test_celery_system_integration.py +++ b/tests/test_celery_system_integration.py @@ -11,6 +11,7 @@ import logging import sys import weakref +from http import HTTPStatus from unittest import mock import pytest @@ -18,6 +19,8 @@ from taskbadger import StatusEnum from taskbadger.celery import Task +from taskbadger.internal.models import TaskRequest, TaskRequestTags +from taskbadger.internal.types import Response from taskbadger.mug import Badger, Settings from taskbadger.systems.celery import CelerySystemIntegration from tests.utils import task_for_test @@ -114,11 +117,8 @@ def add_normal(self, a, b): def test_celery_record_task_args_local_override(celery_session_app, celery_session_worker): """Test that passing `taskbadger_record_task_args` overrides the integration value""" - @celery_session_app.task(bind=True, base=Task) - def add_normal_with_override(self, a, b): - assert self.request.get("taskbadger_task_id") is not None, "missing task in request" - assert hasattr(self, "taskbadger_task") - assert Badger.current.session().client is not None, "missing client" + @celery_session_app.task(base=Task) + def add_normal_with_override(a, b): return a + b celery_session_worker.reload() @@ -128,8 +128,8 @@ def add_normal_with_override(self, a, b): with ( mock.patch("taskbadger.celery.create_task_safe") as create, - mock.patch("taskbadger.celery.update_task_safe") as update, - mock.patch("taskbadger.celery.get_task") as get_task, + mock.patch("taskbadger.celery.update_task_safe"), + mock.patch("taskbadger.celery.get_task"), ): tb_task = task_for_test() create.return_value = tb_task @@ -140,9 +140,45 @@ def add_normal_with_override(self, a, b): create.assert_called_once_with( "tests.test_celery_system_integration.add_normal_with_override", status=StatusEnum.PENDING ) - assert get_task.call_count == 1 - assert update.call_count == 2 - assert Badger.current.session().client is None + + +@pytest.mark.usefixtures("_bind_settings_with_system") +def test_celery_global_tags(celery_session_app, celery_session_worker): + @celery_session_app.task(base=Task) + def add_with_tags(a, b): + return a + b + + celery_session_worker.reload() + Badger.current.scope().tag({"tag1": "value1", "tag2": "value2"}) + + with ( + mock.patch("taskbadger.sdk.task_create.sync_detailed") as create, + mock.patch("taskbadger.celery.update_task_safe"), + mock.patch("taskbadger.celery.get_task"), + ): + tb_task = task_for_test() + create.return_value = Response( + status_code=HTTPStatus.OK, + content=b"", + headers={}, + parsed=tb_task, + ) + # create.return_value = tb_task + result = add_with_tags.delay(2, 2, taskbadger_tags={"tag2": "override"}) + assert result.info.get("taskbadger_task_id") == tb_task.id + assert result.get(timeout=10, propagate=True) == 4 + + request = TaskRequest( + name="tests.test_celery_system_integration.add_with_tags", + status=StatusEnum.PENDING, + tags=TaskRequestTags.from_dict({"tag1": "value1", "tag2": "override"}), + ) + create.assert_called_with( + client=mock.ANY, + organization_slug="org", + project_slug="proj", + body=request, + ) @pytest.mark.parametrize( diff --git a/tests/test_cli_config.py b/tests/test_cli_config.py index aaab4a8..051aab7 100644 --- a/tests/test_cli_config.py +++ b/tests/test_cli_config.py @@ -23,7 +23,12 @@ def mock_config_location(): @pytest.fixture() def _mock_config(mock_config_location): - config = Config(organization_slug="test_org", project_slug="test_project", token="test_token") + config = Config( + organization_slug="test_org", + project_slug="test_project", + token="test_token", + tags={"env": "prod", "host": "localhost"}, + ) write_config(config) @@ -67,7 +72,7 @@ def test_info_args_trump_env(): @pytest.mark.usefixtures("_mock_config") def test_info_config(): result = runner.invoke(app, ["info"]) - _check_output(result, "test_org", "test_project", "test_token") + _check_output(result, "test_org", "test_project", "test_token", tags={"env": "prod", "host": "localhost"}) @mock.patch.dict( @@ -82,13 +87,13 @@ def test_info_config(): @pytest.mark.usefixtures("_mock_config") def test_info_config_env(): result = runner.invoke(app, ["info"]) - _check_output(result, "org2", "project2", "token2") + _check_output(result, "org2", "project2", "token2", tags={"env": "prod", "host": "localhost"}) @pytest.mark.usefixtures("_mock_config") def test_info_config_args(): result = runner.invoke(app, ["-o", "org1", "-p", "project1", "info"]) - _check_output(result, "org1", "project1", "test_token") + _check_output(result, "org1", "project1", "test_token", tags={"env": "prod", "host": "localhost"}) @mock.patch.dict( @@ -103,7 +108,7 @@ def test_info_config_args(): @pytest.mark.usefixtures("_mock_config") def test_info_config_env_args(): result = runner.invoke(app, ["-o", "org1", "-p", "project1", "info"]) - _check_output(result, "org1", "project1", "token2") + _check_output(result, "org1", "project1", "token2", tags={"env": "prod", "host": "localhost"}) def test_configure(mock_config_location): @@ -122,8 +127,13 @@ def test_configure(mock_config_location): } -def _check_output(result, org, project, token): +def _check_output(result, org, project, token, tags=None): assert result.exit_code == 0 assert f"Organization slug: {org}" in result.stdout assert f"Project slug: {project}" in result.stdout assert f"Auth token: {token}" in result.stdout + if tags: + for key, value in tags.items(): + assert f"{key}: {value}" in result.stdout + else: + assert "Tags:" not in result.stdout diff --git a/tests/test_cli_create_update.py b/tests/test_cli_create_update.py index 2be7315..04245d2 100644 --- a/tests/test_cli_create_update.py +++ b/tests/test_cli_create_update.py @@ -8,9 +8,10 @@ from taskbadger.cli_main import app from taskbadger.internal.models import ( PatchedTaskRequest, + PatchedTaskRequestTags, StatusEnum, TaskRequest, - TaskRequestData, + TaskRequestTags, ) from taskbadger.internal.types import Response from tests.utils import task_for_test @@ -19,7 +20,7 @@ @pytest.fixture(autouse=True) -def mock_env(): +def _mock_env(): with mock.patch.dict( os.environ, { @@ -48,6 +49,10 @@ def test_cli_create(): "b=2", "--metadata", "a=3", + "--tag", + "name=foo", + "--tag", + "bar=baz", ] result = runner.invoke(app, args, catch_exceptions=False) assert result.exit_code == 0, result.output @@ -56,13 +61,14 @@ def test_cli_create(): name="my-task", status=StatusEnum.PROCESSING, value_max=100, - data=TaskRequestData.from_dict({"b": "2", "a": 1, "c": 1}), + data={"b": "2", "a": 1, "c": 1}, + tags=TaskRequestTags.from_dict({"name": "foo", "bar": "baz"}), ) create.assert_called_with( client=mock.ANY, organization_slug="org", project_slug="project", - json_body=request, + body=request, ) @@ -73,17 +79,19 @@ def test_cli_update(): result = runner.invoke( app, - ["update", "task123", "--status=success", "--value", "100"], + ["update", "task123", "--status=success", "--value", "100", "--tag", "name=foo"], catch_exceptions=False, ) assert result.exit_code == 0, result.output - body = PatchedTaskRequest(status=StatusEnum.SUCCESS, value=100) + body = PatchedTaskRequest( + status=StatusEnum.SUCCESS, value=100, tags=PatchedTaskRequestTags.from_dict({"name": "foo"}) + ) update.assert_called_with( client=mock.ANY, organization_slug="org", project_slug="project", id="task123", - json_body=body, + body=body, ) diff --git a/tests/test_cli_run.py b/tests/test_cli_run.py index 1b89168..859460e 100644 --- a/tests/test_cli_run.py +++ b/tests/test_cli_run.py @@ -8,9 +8,9 @@ from taskbadger.cli_main import app from taskbadger.internal.models import ( PatchedTaskRequest, - PatchedTaskRequestData, StatusEnum, TaskRequest, + TaskRequestTags, ) from taskbadger.internal.types import UNSET, Response from taskbadger.mug import Badger @@ -38,6 +38,15 @@ def test_cli_run_success(): _test_cli_run(["echo", "test"], 0, args=["task_name"]) +def test_cli_run_tags(): + _test_cli_run( + ["echo", "test"], + 0, + ["task_name", "--tag", "name=value", "--tag", "name1=value1"], + tags={"name": "value", "name1": "value1"}, + ) + + def test_cli_long_run(): def _should_update_task(last_update, update_frequency_seconds): return True @@ -49,13 +58,13 @@ def _should_update_task(last_update, update_frequency_seconds): def test_cli_capture_output(): update_patch = _test_cli_run(["echo test"], 0, args=["task_name", "--capture-output"], update_call_count=2) - body = PatchedTaskRequest(status=UNSET, data=PatchedTaskRequestData.from_dict({"stdout": "test\n"})) + body = PatchedTaskRequest(status=UNSET, data={"stdout": "test\n"}) update_patch.assert_any_call( client=mock.ANY, organization_slug="org", project_slug="project", id=mock.ANY, - json_body=body, + body=body, ) @@ -71,13 +80,13 @@ def _should_update_task(last_update, update_frequency_seconds): update_call_count=3, ) - body = PatchedTaskRequest(status=UNSET, data=PatchedTaskRequestData.from_dict({"stdout": "test\n123\n"})) + body = PatchedTaskRequest(status=UNSET, data={"stdout": "test\n123\n"}) update_patch.assert_any_call( client=mock.ANY, organization_slug="org", project_slug="project", id=mock.ANY, - json_body=body, + body=body, ) @@ -107,7 +116,7 @@ def test_cli_run_webhook(): ) -def _test_cli_run(command, return_code, args=None, action=None, update_call_count=1): +def _test_cli_run(command, return_code, args=None, action=None, tags=None, update_call_count=1): update_mock = mock.MagicMock() def _update(*args, **kwargs): @@ -115,8 +124,8 @@ def _update(*args, **kwargs): update_mock(*args, **kwargs) # handle updating task data - data = kwargs["json_body"].data - task_return = task_for_test(id=task_id, data=data.additional_properties if data else None) + data = kwargs["body"].data + task_return = task_for_test(id=task_id, data=data if data else None) return Response(HTTPStatus.OK, b"", {}, task_return) with ( @@ -134,11 +143,13 @@ def _update(*args, **kwargs): request = TaskRequest(name="task_name", status=StatusEnum.PROCESSING, stale_timeout=10) if action: request.additional_properties = {"actions": [action]} + if tags: + request.tags = TaskRequestTags.from_dict(tags) create.assert_called_with( client=mock.ANY, organization_slug="org", project_slug="project", - json_body=request, + body=request, ) if return_code == 0: @@ -146,7 +157,7 @@ def _update(*args, **kwargs): else: body = PatchedTaskRequest( status=StatusEnum.ERROR, - data=PatchedTaskRequestData.from_dict({"return_code": return_code}), + data={"return_code": return_code}, ) assert update_mock.call_count == update_call_count @@ -155,7 +166,7 @@ def _update(*args, **kwargs): organization_slug="org", project_slug="project", id=task.id, - json_body=body, + body=body, ) return update_mock diff --git a/tests/test_decorators.py b/tests/test_decorators.py index 4ba10ca..f7960bc 100644 --- a/tests/test_decorators.py +++ b/tests/test_decorators.py @@ -20,15 +20,19 @@ def test(arg): @mock.patch("taskbadger.decorators.create_task_safe") def test_track_decorator_args(create): - @track(name="new name", monitor_id="test", max_runtime=1) + @track(name="new name", monitor_id="test", max_runtime=1, tags={"name": "value"}) def test(arg): return arg assert test("test") == "test" assert create.call_count == 1 assert create.call_args.args[0] == "new name" - assert create.call_args.kwargs["monitor_id"] == "test" - assert create.call_args.kwargs["max_runtime"] == 1 + assert create.call_args.kwargs == { + "status": "processing", + "monitor_id": "test", + "max_runtime": 1, + "tags": {"name": "value"}, + } @mock.patch("taskbadger.decorators.create_task_safe") diff --git a/tests/test_scope.py b/tests/test_scope.py index 3e4e47a..5950826 100644 --- a/tests/test_scope.py +++ b/tests/test_scope.py @@ -9,6 +9,7 @@ def test_scope_singleton(): assert Badger.current == GLOBAL_MUG scope = Badger.current.scope() assert scope.context == {} + assert scope.tags == {} assert scope.stack == [] assert scope == Badger.current.scope() @@ -16,25 +17,33 @@ def test_scope_singleton(): def test_scope_context(): scope = Badger.current.scope() assert scope.context == {} + assert scope.tags == {} assert scope.stack == [] with scope: - assert scope.stack == [{}] + assert scope.stack == [({}, {})] scope.context["foo"] = "bar" + scope.tags["name"] = "value" with scope: - assert scope.stack == [{}, {"foo": "bar"}] + assert scope.stack == [({}, {}), ({"foo": "bar"}, {"name": "value"})] assert scope.context == {"foo": "bar"} + assert scope.tags == {"name": "value"} scope.context["bar"] = "bazz" + scope.tags["bar"] = "bazz" with scope: assert scope.context == {"foo": "bar", "bar": "bazz"} + assert scope.tags == {"name": "value", "bar": "bazz"} scope.context.clear() + scope.tags.clear() assert scope.context == {"foo": "bar"} - assert scope.stack == [{}] + assert scope.tags == {"name": "value"} + assert scope.stack == [({}, {})] assert scope.context == {} + assert scope.tags == {} assert scope.stack == [] @pytest.fixture(autouse=True) -def init_skd(): +def _init_skd(): init("org", "project", "token") @@ -42,13 +51,16 @@ def test_create_task_with_scope(httpx_mock): with Badger.current.scope() as scope: scope["foo"] = "bar" scope["bar"] = "bazz" + scope.tag({"name": "value"}) httpx_mock.add_response( url="https://taskbadger.net/api/org/project/tasks/", method="POST", match_headers={"Authorization": "Bearer token"}, - match_content=b'{"name": "name", "status": "pending", "data": {"foo": "bar", "bar": "buzzer"}}', + match_content=b'{"name": "name", "status": "pending", ' + b'"data": {"foo": "bar", "bar": "buzzer"}, ' + b'"tags": {"name": "value", "name1": "value1"}}', json=_json_task_response(), status_code=201, ) - task = create_task("name", data={"bar": "buzzer"}) + task = create_task("name", data={"bar": "buzzer"}, tags={"name1": "value1"}) _verify_task(task) diff --git a/tests/test_sdk.py b/tests/test_sdk.py index fe7fed0..bd0023b 100644 --- a/tests/test_sdk.py +++ b/tests/test_sdk.py @@ -7,9 +7,7 @@ from taskbadger.exceptions import TaskbadgerException from taskbadger.internal.models import ( PatchedTaskRequest, - PatchedTaskRequestData, TaskRequest, - TaskRequestData, ) from taskbadger.internal.types import UNSET, Response from taskbadger.mug import Badger @@ -76,7 +74,7 @@ def test_create(settings, patched_create): status=StatusEnum.PRE_PROCESSING, value=13, value_max=UNSET, - data=TaskRequestData.from_dict(data), + data=data, max_runtime=10, stale_timeout=2, ) @@ -93,7 +91,7 @@ def test_create(settings, patched_create): client=mock.ANY, organization_slug="org", project_slug="project", - json_body=request, + body=request, ) @@ -188,7 +186,7 @@ def _verify_update(settings, patched_update, **kwargs): request_params.update(kwargs) if kwargs.get("data"): - request_params["data"] = PatchedTaskRequestData.from_dict(kwargs["data"]) + request_params["data"] = kwargs["data"] request = PatchedTaskRequest(**request_params) if actions: @@ -200,5 +198,5 @@ def _verify_update(settings, patched_update, **kwargs): organization_slug="org", project_slug="project", id=mock.ANY, - json_body=request, + body=request, ) diff --git a/tests/test_sdk_primatives.py b/tests/test_sdk_primatives.py index cfdf10f..1039c82 100644 --- a/tests/test_sdk_primatives.py +++ b/tests/test_sdk_primatives.py @@ -8,7 +8,7 @@ @pytest.fixture(autouse=True) -def init_skd(): +def _init_skd(): init("org", "project", "token") @@ -75,6 +75,7 @@ def test_update_task(httpx_mock): "value": 150, "value_max": 150, "data": {"custom": "value"}, + "tags": {"tag": "value"}, } httpx_mock.add_response( url="https://taskbadger.net/api/org/project/tasks/test_id/", @@ -128,6 +129,7 @@ def _json_task_response(**kwargs): "updated": "2022-09-22T06:53:40.683555Z", "url": None, "public_url": None, + "tags": {"tag": "value"}, } response.update(kwargs) return response @@ -144,3 +146,4 @@ def _verify_task(task, **kwargs): assert task.value_max == expected["value_max"] assert task.value_percent == expected["value_percent"] assert task.data == expected["data"] + assert task.tags == expected["tags"] diff --git a/tests/test_session.py b/tests/test_session.py index 8ab7039..2cdcb5d 100644 --- a/tests/test_session.py +++ b/tests/test_session.py @@ -8,7 +8,7 @@ @pytest.fixture(autouse=True) -def bind_settings(): +def _bind_settings(): Badger.current.bind(Settings("https://taskbadger.net", "token", "org", "proj")) diff --git a/tests/utils.py b/tests/utils.py index 8df3a65..2453a4a 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -2,23 +2,22 @@ from uuid import uuid4 from taskbadger.internal.models import Task as TaskInternal -from taskbadger.internal.models import TaskData def task_for_test(**kwargs): task_id = kwargs.pop("id", uuid4().hex) data = kwargs.pop("data", None) if data: - kwargs["data"] = TaskData.from_dict(data) + kwargs["data"] = data kwargs["url"] = None kwargs["public_url"] = None kwargs["value_percent"] = None + kwargs["created"] = datetime.utcnow() + kwargs["updated"] = datetime.utcnow() return TaskInternal( task_id, "org", "project", "task_name", - datetime.utcnow(), - datetime.utcnow(), **kwargs, )