# Assetor Review Guidelines Guidelines derived from 28 merged MRs of real code review history. Newer reviews take precedence. --- ## Code Style & Naming - **AVOID** returning multi-value tuples from methods when a named dataclass would be clearer. Tuple destructuring at the call site is hard to read and fragile. ```python # AVOID (asset_per_device_id, company_brand_ids, failed_responses) = await manager.bulk_create_preload(body, user_id) # DO instead bulk_preload: BulkPreloadDto = await manager.bulk_create_preload(body, user_id) ``` Use `@dataclass` (not Pydantic) for internal result objects to avoid forward-ref issues. Pair with `from __future__ import annotations` and `TYPE_CHECKING` guards for circular-import-safe type hints. (MR !3) - **DO** use typed dataclasses for structured error/response payloads rather than plain dicts. ```python # AVOID failed_responses.append({"device_id": device.id, "reason": "..."}) # DO instead @dataclass class FailedResponse: device_id: int reason: str # Consider static factory methods for repeated reason strings FailedResponse.for_device_already_has_asset(device_id) ``` (MR !3) - **DO** use `Iterable[T]` for input parameters that only need to be iterated, not `List[T]`. Accept the most general type; return the most specific type. (MR !3) - **DO** name filter/query variables accurately. A misspelled variable like `asset_item_filer` must be corrected before merge. (MR !21) - **DO** order method parameters hierarchically: `tenant_id` before `company_id`, more general identifiers before more specific ones. Apply consistently across all call sites and signatures. (MR !21) - **AVOID** `datetime.utcnow()` in new code. Use `datetime.now(UTC)` — `utcnow()` is deprecated in Python 3.12+. (MR !25) - **AVOID** broad ruff/mypy ignore rules in `pyproject.toml`. Do not suppress entire error categories without confirming they are genuinely needed — run `pre-commit run --all-files` to verify. (MR !25) --- ## API Design - **DO** return `404` (not `400` or `500`) when a referenced object is not found on POST and PATCH endpoints. (MR !13) - **DO** keep `_update_fields_from_dto()` methods focused. Extract multi-step sub-operations (e.g. warehouse update, master_system_id update) into their own dedicated `_update_()` private methods rather than embedding logic inline. (MR !21) - **DO** use the correct auth client factory for each context: - User token required: `AuthServiceAPIClientFactory.create_user_client(...)` → `AuthorizationServiceAPIClient` - No authorization: `AuthServiceAPIClientFactory.create_public_client(...)` → `AuthorizationServicePublicAPIClient` Do not mix up the two client types. (MR !29) - **DO** use SQLAlchemy 2.x style in all new queries: - `select(Model)` not `select([Model])` - `(condition, value)` not `[(condition, value)]` in CASE expressions - Add `.mappings()` to raw SQL results for dictionary-like access (MR !29) --- ## Error Handling & Exceptions - **DO** narrow `try/except` blocks to only the line(s) that can raise the caught exception. Keep all subsequent logic outside the `try` block. ```python # AVOID try: assignment = await self._get_asset_machine(asset) if assignment.machine_id is None: raise AssetItemNotAssigned(asset.id) if machine.id != assignment.machine_id: raise AssetItemAssignedToDifferentMachine(asset.id) except ObjectNotFound: raise AssetItemNotAssigned(asset.id) # DO instead try: assignment = await self._get_asset_machine(asset) except ObjectNotFound: raise AssetItemNotAssigned(asset.id) if assignment.machine_id is None: raise AssetItemNotAssigned(asset.id) if machine.id != assignment.machine_id: raise AssetItemAssignedToDifferentMachine(asset.id) ``` (MR !5) - **DO** handle external service unavailability explicitly. Whenever calling an external service (e.g. fiscal service), add error handling for the unreachable case. (MR !21) --- ## Testing - **DO** put manager-level behaviour tests in `test__manager.py`, not `test__endpoints.py`. Endpoint tests should only exercise code that lives in the endpoint itself. Manager tests are faster and easier to isolate. (MR !21) - **DO** cover each status transition (e.g. `REPARATION → IN_USE`, `operation_status` changes) with a dedicated test. (MRs !5, !19) - **DO** ensure the test DB name in default settings matches the actual local database name used after a fresh clone. (MR !25) --- ## Architecture & Domain Rules - **DO** resolve circular imports using `TYPE_CHECKING` guards and `from __future__ import annotations`, not by loosening types to `Any`: ```python from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: from televend_core.databases.televend_repositories.asset_item.model import AssetItem from televend_core.databases.televend_repositories.device import Device ``` Use `@dataclass` (stdlib) rather than Pydantic for internal DTOs when forward references cause Pydantic config errors. (MR !3) - **DO** silently create dependent records (e.g. `AssetItemFiscalItalyDevice`) when missing during an update that requires them. Document this in the method docstring. (MR !22) - **DO** validate that `is_fiscal=True` is never set on device types (e.g. banknote acceptors) that cannot be fiscal. Enforce at the manager layer. (MRs !14, !15) - **DO** look up users by email (unique cross-tenant identifier) rather than by `cloud_user_id` in token-based auth flows. (MR !9) --- ## Git & Process - **DO** include the Jira ticket ID in both the branch name and every commit message: `feature/CLOUD-XXXXX-description`, commit `CLOUD-XXXXX: description`. (all MRs) - **DO** add `/.venv` to `.gitignore`. (MR !25) - **DO** remove unused or transitively-provided dependencies from `pyproject.toml`. (MR !25) - **DO** bump the service version in `pyproject.toml` as part of any dependency-upgrade MR. (MR !25) - **DO** update `README.md` quickstart section when performing a major runtime upgrade (e.g. Python 3.12 migration). (MR !25) - **AVOID** `event_loop` fixture overrides in `conftest.py` — no longer required with modern `pytest-asyncio`. Remove during any Python 3.12 upgrade. (MR !25)