""" Endpoints Microsoft Graph para Planner. Formato oficial: {HTTP method} https://graph.microsoft.com/{version}/{resource}?{query-parameters} Documentacion: https://learn.microsoft.com/graph/api/overview """ from dataclasses import dataclass, field from typing import Any @dataclass(frozen=True) class GraphEndpoint: method: str resource: str query_parameters: dict[str, str] = field(default_factory=dict) description: str = "" def path(self) -> str: return f"/{self.resource.lstrip('/')}" def label(self, version: str = "v1.0") -> str: query = "&".join(f"{k}={v}" for k, v in self.query_parameters.items()) base = f"https://graph.microsoft.com/{version}/{self.resource.lstrip('/')}" if query: return f"{self.method} {base}?{query}" return f"{self.method} {base}" # --- Endpoints modo delegado (tu cuenta, sin admin) --- ME_PROFILE = GraphEndpoint( method="GET", resource="me", description="Perfil del usuario autenticado", ) ME_PLANNER_PLANS = GraphEndpoint( method="GET", resource="me/planner/plans", description="Planes de Planner a los que tienes acceso", ) ME_JOINED_TEAMS = GraphEndpoint( method="GET", resource="me/joinedTeams", description="Equipos de Microsoft Teams donde eres miembro", ) # --- Endpoints de Planner (ambos modos) --- def plan_tasks(plan_id: str) -> GraphEndpoint: return GraphEndpoint( method="GET", resource=f"planner/plans/{plan_id}/tasks", description=f"Tareas del plan {plan_id}", ) def plan_buckets(plan_id: str) -> GraphEndpoint: return GraphEndpoint( method="GET", resource=f"planner/plans/{plan_id}/buckets", description=f"Columnas/buckets del plan {plan_id}", ) def task_details(task_id: str) -> GraphEndpoint: return GraphEndpoint( method="GET", resource=f"planner/tasks/{task_id}/details", description=f"Detalle de la tarea {task_id}", ) # --- Endpoints modo aplicacion (requiere admin) --- GROUPS_WITH_TEAMS = GraphEndpoint( method="GET", resource="groups", query_parameters={ "$filter": "resourceProvisioningOptions/Any(x:x eq 'Team')", "$select": "id,displayName,description", }, description="Grupos de M365 con Teams (requiere Group.Read.All)", ) def group_plans(group_id: str) -> GraphEndpoint: return GraphEndpoint( method="GET", resource=f"groups/{group_id}/planner/plans", description=f"Planes del grupo {group_id}", ) # Secuencia de prueba por modo DELEGATED_PROBE_SEQUENCE: list[GraphEndpoint] = [ ME_PROFILE, ME_JOINED_TEAMS, ME_PLANNER_PLANS, ] APPLICATION_PROBE_SEQUENCE: list[GraphEndpoint] = [ GROUPS_WITH_TEAMS, ]