From d042e154c93290be64b0888edd202d2f48dd04b6 Mon Sep 17 00:00:00 2001 From: Wagner Elias Date: Mon, 2 Mar 2026 09:56:06 -0300 Subject: [PATCH 1/2] Add a filter to assignee in projects --- README.md | 2 ++ src/conviso/commands/projects.py | 38 ++++++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2dc037e..67be28c 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ conviso --help ## Usage (examples) - Projects: `python -m conviso.app projects list --company-id 443 --all` +- Projects (assignee filter): `python -m conviso.app projects list --company-id 443 --filter assignee=analyst@company.com --all` - Project requirements + activities: `python -m conviso.app projects requirements --project-id 12345` - Assets: `python -m conviso.app assets list --company-id 443 --tags cloud --attack-surface INTERNET_FACING --all` - Requirements: `python -m conviso.app requirements create --company-id 443 --label "Req" --description "Desc" --activity "Login|Check login|REF-123"` @@ -100,6 +101,7 @@ conviso --help Output options: `--format table|json|csv`, `--output path` to save JSON/CSV. Notes: +- `projects list --filter` supports `assignee=` to filter by allocated analyst. - GraphQL errors return exit code 1. - Use `--all` on list commands to fetch every page. - `--quiet` silences info logs; `--verbose` shows per-page requests when paginating. diff --git a/src/conviso/commands/projects.py b/src/conviso/commands/projects.py index 4a1c5c4..c7ada88 100644 --- a/src/conviso/commands/projects.py +++ b/src/conviso/commands/projects.py @@ -26,7 +26,7 @@ def list_projects( None, "--filter", "-F", - help="Apply filters in 'field=value' format. Supports aliases (e.g., id=123, name=foo, status=DONE).", + help="Apply filters in 'field=value' format. Supports aliases (e.g., id=123, name=foo, status=DONE, assignee=user@company.com).", ), sort_by: Optional[str] = typer.Option( None, @@ -46,14 +46,20 @@ def list_projects( # Build search parameters params = {"scopeIdEq": company_id} + assignee_filter = None if filters: for f in filters: if "=" not in f: warning(f"[WARN] Invalid filter syntax: {f} (expected key=value)") continue key, value = f.split("=", 1) - gql_key = schema.resolve_filter_key(key.strip()) - casted = schema.cast_filter_value(gql_key, value.strip()) + key = key.strip() + value = value.strip() + if key.lower() == "assignee": + assignee_filter = value.lower() + continue + gql_key = schema.resolve_filter_key(key) + casted = schema.cast_filter_value(gql_key, value) params[gql_key] = casted variables = { @@ -90,6 +96,9 @@ def list_projects( environmentCompromised projectType { label } tags { name } + allocatedAnalyst { + portalUser { name email } + } assets { id name } requirementsProgress { done total } } @@ -99,6 +108,7 @@ def list_projects( """ try: + fetch_all = all_pages or bool(assignee_filter) current_page = page rows = [] total_pages = None @@ -119,6 +129,21 @@ def list_projects( break for p in collection: + assignees = [] + for alloc in p.get("allocatedAnalyst") or []: + portal_user = (alloc or {}).get("portalUser") or {} + email = (portal_user.get("email") or "").strip() + name = (portal_user.get("name") or "").strip() + if email: + assignees.append(email) + elif name: + assignees.append(name) + + if assignee_filter: + haystack = " ".join(assignees).lower() + if assignee_filter not in haystack: + continue + done = (p.get("requirementsProgress") or {}).get("done", 0) total = (p.get("requirementsProgress") or {}).get("total", 0) requirements = f"{done}/{total}" @@ -148,10 +173,13 @@ def list_projects( "tags": tags_str, }) - if not all_pages or (total_pages is not None and current_page >= total_pages): + if not fetch_all or (total_pages is not None and current_page >= total_pages): break current_page += 1 + if not rows: + typer.echo("⚠️ No projects found.") + raise typer.Exit() export_data( rows, @@ -175,6 +203,8 @@ def list_projects( f"(page {page}/{total_pages_calc}).\n" ) + except typer.Exit: + raise except Exception as e: error(f"Error listing projects: {e}") raise typer.Exit(code=1) From d7f499092477b015da2925a4630073587a3346fe Mon Sep 17 00:00:00 2001 From: Wagner Elias Date: Mon, 2 Mar 2026 09:57:39 -0300 Subject: [PATCH 2/2] chore(release): bump version to 0.3.1 --- src/conviso/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conviso/VERSION b/src/conviso/VERSION index 0d91a54..9e11b32 100644 --- a/src/conviso/VERSION +++ b/src/conviso/VERSION @@ -1 +1 @@ -0.3.0 +0.3.1