o
    .j^`                     @  s`  d Z ddlmZ ddlZddlZzddlZddlmZ W n e	y/   ddlZddl
mZ Y nw ddlmZmZ ddlmZmZmZmZmZmZmZmZmZ ddlmZmZmZmZmZmZmZ ddl m!Z!m"Z"m#Z# dd	l$m%Z% dd
l&m'Z'm(Z( ddl)m*Z* ddl+m,Z, ddl-m.Z.m/Z/ ddl0m1Z1 e2dZ3e"e!gZ4e"e#gZ5G dd dZ6G dd de6Z7dS )a   
Synchronous JMAP client.

Wraps session establishment, HTTP communication, and method dispatching
into a single object with a clean public API.

Auth note: JMAP has no 401-challenge-retry dance (unlike CalDAV).
Credentials are sent upfront on every request. A 401/403 is a hard failure.
    )annotationsN)HTTPBasicAuth)build_calendar_getparse_calendar_get)	build_event_changesbuild_event_getbuild_event_querybuild_event_set_createbuild_event_set_destroybuild_event_set_updateparse_event_changesparse_event_getparse_event_set)build_task_getbuild_task_list_getbuild_task_set_createbuild_task_set_destroybuild_task_set_updateparse_task_list_getparse_task_set)CALENDAR_CAPABILITYCORE_CAPABILITYTASK_CAPABILITY)ical_to_jscal)JMAPAuthErrorJMAPMethodError)JMAPCalendarJMAPCalendarObject)Sessionfetch_session)HTTPBearerAuthzcaldav.jmapc                   @  sD   e Zd Z					dd ddZd!ddZd"ddZed#ddZdS )$_JMAPClientBaseN   urlstrusername
str | Nonepassword	auth_typetimeoutintreturnNonec                 C  s@   || _ || _|| _|| _d | _|d ur|| _d S | || _d S N)r$   r&   r(   r*   _session_cache_auth_build_auth)selfr$   r&   r(   authr)   r*    r4   G/home/thesage/.local/lib/python3.10/site-packages/caldav/jmap/client.py__init__;   s   	
z_JMAPClientBase.__init__c                 C  s   |}|du r| j rd}n| jrd}nt| jdd|dkr1| j r#| js*t| jddt| j | jS |dkrD| js?t| jddt| jS t| jd|d	d)
a  Select and construct the auth object.

        **The JMAP support is experimental, the API may change in minor-releases**

        JMAP supports Basic and Bearer auth; Digest is not supported.
        When ``auth_type`` is ``None`` the type is inferred from the
        credentials supplied: a username triggers Basic, a password
        alone triggers Bearer, and neither raises :class:`JMAPAuthError`.
        NbasicbearerzDNo credentials provided. Supply username+password or a bearer token.r$   reasonz/Basic auth requires both username and password.z?Bearer auth requires a token supplied as the password argument.zUnsupported auth_type z. Use 'basic' or 'bearer'.)r&   r(   r   r$   r   r!   )r2   r)   effective_typer4   r4   r5   r1   O   s8   


z_JMAPClientBase._build_authsessionr   errdictc                 C  s   t |jd| |ddd)Nzset failed: typeserverErrorr$   r:   
error_type)r   api_urlget)r2   r<   r=   r4   r4   r5   _raise_set_errory   s
   
z _JMAPClientBase._raise_set_error
account_idcalendar_idstartendtextlist[tuple]c                 C  sv   i }|dur|g|d< |dur||d< |dur||d< |dur#||d< t | |p(dd}d| dd	d
dddf}||gS )zPReturn a batched [CalendarEvent/query, CalendarEvent/get] call list for _search.NinCalendarsafterbeforerJ   )filter_conditionCalendarEvent/getz
ev-query-0zCalendarEvent/queryz/ids)resultOfnamepath)	accountIdz#idszev-get-1)r   )rF   rG   rH   rI   rJ   filter_dict
query_callget_callr4   r4   r5   _build_event_search_calls   s(   	
z)_JMAPClientBase._build_event_search_calls)NNNNr#   )r$   r%   r&   r'   r(   r'   r)   r'   r*   r+   r,   r-   )r)   r'   )r<   r   r=   r>   r,   r-   )rF   r%   rG   r'   rH   r'   rI   r'   rJ   r'   r,   rK   )__name__
__module____qualname__r6   r1   rE   staticmethodrX   r4   r4   r4   r5   r"   :   s    

*r"   c                   @  s   e Zd ZdZdGddZdHddZdId	d
ZdJdKddZdLddZdMddZ	dNddZ
dOdd Z					dPdQd(d)Z				dRdSd*d+ZdTd,d-ZdUd0d1ZdVd2d3Z	dWdXd5d6ZdYd8d9ZdZd<d=Zd[d@dAZd\dCdDZd]dEdFZdS )^
JMAPClienta  Synchronous JMAP client for calendar operations.

    Usage::

        from caldav.jmap import get_jmap_client
        client = get_jmap_client(url="https://jmap.example.com/.well-known/jmap",
                                  username="alice", password="secret")
        calendars = client.get_calendars()

    Args:
        url: URL of the JMAP session endpoint (``/.well-known/jmap``).
        username: Username for Basic auth.
        password: Password for Basic auth, or bearer token if no username.
        auth: A pre-built requests-compatible auth object. Takes precedence
              over username/password if provided.
        auth_type: Force a specific auth type: ``"basic"`` or ``"bearer"``.
        timeout: HTTP request timeout in seconds.
    r,   c                 C  s   | S r.   r4   r2   r4   r4   r5   	__enter__      zJMAPClient.__enter__r-   c                 C  s   d S r.   r4   )r2   exc_typeexc_valexc_tbr4   r4   r5   __exit__   r`   zJMAPClient.__exit__r   c                 C  s&   | j du rt| j| j| jd| _ | j S )z5Return the cached Session, fetching it on first call.N)r3   r*   )r/   r    r$   r0   r*   r^   r4   r4   r5   _get_session   s   
zJMAPClient._get_sessionNmethod_callsrK   usinglist[str] | Nonelistc                 C  s   |   }|dur
|ntt|d}td|jt| tj|j|| j	ddd| j
d}|jdv r<t|jd|j d	d
|  | }|dg }|D ]}|\}	}
}|	dkrh|
dd}t|jd|
 |dqL|S )a  POST a batch of JMAP method calls and return the methodResponses.

        Args:
            method_calls: List of 3-tuples ``(method_name, args_dict, call_id)``.
            using: Capability URN list for the ``using`` field. Defaults to
                ``_DEFAULT_USING`` (core + calendars).

        Returns:
            List of 3-tuples ``(method_name, response_args, call_id)`` from
            the server's ``methodResponses`` array.

        Raises:
            JMAPAuthError: On HTTP 401 or 403.
            JMAPMethodError: If any methodResponse is an ``error`` response.
            requests.HTTPError: On other non-2xx HTTP responses.
        N)rg   methodCallsz"JMAP POST to %s: %d method call(s)zapplication/json)zContent-TypeAccept)jsonr3   headersr*   )i  i  zHTTP z from API endpointr9   methodResponseserrorr?   r@   zMethod call failed: rA   )re   _DEFAULT_USINGri   logdebugrC   lenrequestspostr0   r*   status_coder   raise_for_statusrl   rD   r   )r2   rf   rg   r<   payloadresponsedatamethod_responsesrespmethod_name	resp_argscall_idrB   r4   r4   r5   _request   s>   

zJMAPClient._requestlist[JMAPCalendar]c           	      C  s`   |   }t|j}| |g}|D ]\}}}|dkr-t|}|D ]}| |_d|_q |  S qg S )zFetch all calendars for the authenticated account.

        Returns:
            List of :class:`~caldav.jmap.objects.calendar.JMAPCalendar` objects.
        zCalendar/getF)re   r   rF   r   r   _client	_is_async)	r2   r<   call	responsesr}   r~   _	calendarscalr4   r4   r5   get_calendars   s   
zJMAPClient.get_calendarsrG   r%   ical_strc                 C  s   |   }t||d}t|jd|i}| |g}|D ]2\}}}	|dkrLt|\}
}	}	}}	}	d|v r9| ||d  d|
vrDt|jdd|
d d   S qt|jdd)ai  Create a calendar event from an iCalendar string.

        Args:
            calendar_id: The JMAP calendar ID to create the event in.
            ical_str: A VCALENDAR string representing the event.

        Returns:
            The server-assigned JMAP event ID.

        Raises:
            JMAPMethodError: If the server rejects the create request.
        )rG   new-0CalendarEvent/setz:CalendarEvent/set response missing created entry for new-0r9   idNo CalendarEvent/set response)	re   r   r	   rF   r   r   rE   r   rC   )r2   rG   r   r<   jscalr   r   r}   r~   r   creatednot_createdr4   r4   r5   create_event  s"   zJMAPClient.create_eventevent_idr   c           	      C  s   |   }t|j|gd}| |g}|D ]$\}}}|dkr8t|}|s.t|jd| ddt|d dd  S qt|jd	d
)a  Fetch a calendar event by JMAP event ID.

        Args:
            event_id: The JMAP event ID to retrieve.

        Returns:
            A :class:`~caldav.jmap.objects.calendar_object.JMAPCalendarObject`
            wrapping the raw JSCalendar dict.  ``parent`` is ``None`` since
            no :class:`~caldav.jmap.objects.calendar.JMAPCalendar` is available
            at the client level.

        Raises:
            JMAPMethodError: If the event is not found.
        idsrP   zEvent not found: notFoundrA   r   Nrz   parentNo CalendarEvent/get responser9   )re   r   rF   r   r   r   rC   r   )	r2   r   r<   r   r   r}   r~   r   itemsr4   r4   r5   	get_event/  s   
zJMAPClient.get_eventc                 C  s   |   }t|}|dd t|j||i}| |g}|D ]"\}}}	|dkr@t|\}	}	}	}	}
}	||
v r=| ||
|   dS qt|j	dd)a  Update a calendar event from an iCalendar string.

        Args:
            event_id: The JMAP event ID to update.
            ical_str: A VCALENDAR string with the updated event data.

        Raises:
            JMAPMethodError: If the server rejects the update.
        uidNr   r   r9   )
re   r   popr   rF   r   r   rE   r   rC   )r2   r   r   r<   patchr   r   r}   r~   r   not_updatedr4   r4   r5   update_eventO  s   
zJMAPClient.update_eventr'   rH   rI   rJ   r   JMAPCalendar | Nonelist[JMAPCalendarObject]c                   s\   |   }| |j||||}| |}|D ]\}	}
}|	dkr+ fddt|
D   S qg S )NrP   c                   s   g | ]}t | d qS )r   r   ).0itemr   r4   r5   
<listcomp>v  s    
z&JMAPClient._search.<locals>.<listcomp>)re   rX   rF   r   r   )r2   rG   rH   rI   rJ   r   r<   callsr   r}   r~   r   r4   r   r5   _searchh  s   


zJMAPClient._searchc                 C  s   | j ||||dS )a  Search for calendar events.

        All parameters are optional; omitting all returns every event in the account.
        Results are fetched in a single batched JMAP request using a result reference
        from ``CalendarEvent/query`` into ``CalendarEvent/get``.

        Args:
            calendar_id: Limit results to this calendar.
            start: Only events ending after this datetime (``YYYY-MM-DDTHH:MM:SS``).
            end: Only events starting before this datetime (``YYYY-MM-DDTHH:MM:SS``).
            text: Free-text search across title, description, locations, and participants.

        Returns:
            List of :class:`~caldav.jmap.objects.calendar_object.JMAPCalendarObject`
            instances.  ``parent`` is ``None`` on these objects since no
            :class:`~caldav.jmap.objects.calendar.JMAPCalendar` is available at
            the client level; use :meth:`JMAPCalendar.search` if you need ``parent``
            set.
        )rG   rH   rI   rJ   )r   )r2   rG   rH   rI   rJ   r4   r4   r5   search_events}  s   zJMAPClient.search_eventsc                 C  sX   |   }t|jg d}| |g}|D ]\}}}|dkr$|dd  S qt|jdd)uy  Return the current CalendarEvent state string for use as a sync token.

        Calls ``CalendarEvent/get`` with an empty ID list — no event data is
        transferred, only the ``state`` field from the response.

        Returns:
            Opaque state string. Pass to :meth:`get_objects_by_sync_token` to
            retrieve only what changed since this point.
        r   rP   state r   r9   )re   r   rF   r   rD   r   rC   r2   r<   r   r   r}   r~   r   r4   r4   r5   get_sync_token  s   
zJMAPClient.get_sync_token
sync_tokenDtuple[list[JMAPCalendarObject], list[JMAPCalendarObject], list[str]]c                   s  |   }t|j|}| |g}g }g }g }|D ]\}}	}
|dkr5t|	\}
}
}}}}|r5t|jdddq|| }|sAg g |fS t|j|d}| |g}i  |D ]\}}	}
|dkrlt|	D ]}t	|dd |d	 < q_qR fd
d|D } fdd|D }|||fS )a  Fetch events changed since a previous sync token.

        Calls ``CalendarEvent/changes`` to discover which events were created,
        modified, or destroyed since ``sync_token`` was issued. Created and
        modified events are returned as
        :class:`~caldav.jmap.objects.calendar_object.JMAPCalendarObject` instances;
        destroyed events are returned as IDs (the objects no longer exist on the server).

        Args:
            sync_token: A state string previously returned by :meth:`get_sync_token`
                or by a prior call to this method.

        Returns:
            A 3-tuple ``(added, modified, deleted)``:

            - ``added``: objects for newly created events (``parent`` is ``None``).
            - ``modified``: objects for updated events (``parent`` is ``None``).
            - ``deleted``: Event IDs that were destroyed.

        Raises:
            JMAPMethodError: If the server reports ``hasMoreChanges: true``.
        zCalendarEvent/changeszCalendarEvent/changes response was truncated by the server (hasMoreChanges=true). Call get_sync_token() to obtain a fresh baseline and re-sync.serverPartialFailrA   r   rP   Nr   r   c                      g | ]
}| v r | qS r4   r4   r   ievents_by_idr4   r5   r         z8JMAPClient.get_objects_by_sync_token.<locals>.<listcomp>c                   r   r4   r4   r   r   r4   r5   r     r   )
re   r   rF   r   r   r   rC   r   r   r   )r2   r   r<   changes_callr   created_idsupdated_ids	destroyedr}   r~   r   has_more	fetch_idsrW   get_responsesr   addedmodifiedr4   r   r5   get_objects_by_sync_token  s<   


z$JMAPClient.get_objects_by_sync_tokenc           	      C  sz   |   }t|j|g}| |g}|D ]"\}}}|dkr5t|\}}}}}}||v r2| |||   dS qt|jdd)zDelete a calendar event.

        Args:
            event_id: The JMAP event ID to delete.

        Raises:
            JMAPMethodError: If the server rejects the delete.
        r   Nr   r9   )re   r
   rF   r   r   rE   r   rC   )	r2   r   r<   r   r   r}   r~   r   not_destroyedr4   r4   r5   delete_event  s   	zJMAPClient.delete_eventr   c                 C  sD   | j ||dD ]}|jd|kr|  S qt|  jd| d)N)rG   r   r   z#No calendar object found with UID: r9   )r   rz   rD   r   re   rC   )r2   r   rG   r   objr4   r4   r5   _get_object_by_uid  s   zJMAPClient._get_object_by_uid
list[dict]c                 C  sJ   |   }t|j}| j|gtd}|D ]\}}}|dkr"t|  S qg S )zFetch all task lists for the authenticated account.

        Returns:
            List of raw JMAP TaskList dicts as returned by the server.
        rg   zTaskList/get)re   r   rF   r   _TASK_USINGr   r   r4   r4   r5   get_task_lists  s   
zJMAPClient.get_task_liststask_list_idtitlec                 K  s   |   }dtt ||dddd}|| t|jd|i}| j|gtd}|D ]'\}}	}
|dkrPt	|	\}}
}
}}
}
d|v rH| 
||d  |d d   S q)t|jd	d
)a,  Create a task in a task list.

        Args:
            task_list_id: The JMAP task list ID to create the task in.
            title: Task title (maps to VTODO ``SUMMARY``).
            **kwargs: Optional JMAP Task fields using wire names: ``description``,
                ``due``, ``start``, ``timeZone``, ``estimatedDuration``,
                ``percentComplete``, ``progress``, ``priority``.

        Returns:
            The server-assigned JMAP task ID.

        Raises:
            JMAPMethodError: If the server rejects the create request.
        Taskr   zneeds-action)z@typer   
taskListIdr   percentCompleteprogresspriorityr   r   Task/setr   No Task/set responser9   )re   r%   uuiduuid4updater   rF   r   r   r   rE   r   rC   )r2   r   r   kwargsr<   	task_dictr   r   r}   r~   r   r   r   r4   r4   r5   create_task  s(   

	zJMAPClient.create_tasktask_idr>   c           	      C  s   |   }t|j|gd}| j|gtd}|D ]"\}}}|dkr8|dg }|s2t|jd| dd|d   S qt|jd	d
)zFetch a task by ID.

        Args:
            task_id: The JMAP task ID to retrieve.

        Returns:
            Raw JMAP Task dict as returned by the server.

        Raises:
            JMAPMethodError: If the task is not found.
        r   r   zTask/getri   zTask not found: r   rA   r   zNo Task/get responser9   )re   r   rF   r   r   rD   r   rC   )	r2   r   r<   r   r   r}   r~   r   r   r4   r4   r5   get_taskD  s   
zJMAPClient.get_taskr   c           
      C  s   |   }t|j||i}| j|gtd}|D ]"\}}}|dkr8t|\}}}}}	}||	v r5| ||	|   dS qt|jdd)a  Update a task with a partial patch.

        Args:
            task_id: The JMAP task ID to update.
            patch: Partial patch dict mapping property names to new values.

        Raises:
            JMAPMethodError: If the server rejects the update.
        r   r   Nr   r9   )	re   r   rF   r   r   r   rE   r   rC   )
r2   r   r   r<   r   r   r}   r~   r   r   r4   r4   r5   update_taska  s   
zJMAPClient.update_taskc           	      C  s~   |   }t|j|g}| j|gtd}|D ]"\}}}|dkr7t|\}}}}}}||v r4| |||   dS qt|jdd)zDelete a task.

        Args:
            task_id: The JMAP task ID to delete.

        Raises:
            JMAPMethodError: If the server rejects the delete.
        r   r   Nr   r9   )	re   r   rF   r   r   r   rE   r   rC   )	r2   r   r<   r   r   r}   r~   r   r   r4   r4   r5   delete_taskx  s   	zJMAPClient.delete_task)r,   r]   )r,   r-   )r,   r   r.   )rf   rK   rg   rh   r,   ri   )r,   r   )rG   r%   r   r%   r,   r%   )r   r%   r,   r   )r   r%   r   r%   r,   r-   )NNNNN)rG   r'   rH   r'   rI   r'   rJ   r'   r   r   r,   r   )NNNN)
rG   r'   rH   r'   rI   r'   rJ   r'   r,   r   )r,   r%   )r   r%   r,   r   )r   r%   r,   r-   )NN)r   r%   rG   r'   r   r   r,   r   )r,   r   )r   r%   r   r%   r,   r%   )r   r%   r,   r>   )r   r%   r   r>   r,   r-   )r   r%   r,   r-   )rY   rZ   r[   __doc__r_   rd   re   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r4   r4   r4   r5   r]      s>    



9

 
 


@


'
r]   )8r   
__future__r   loggingr   niquestsrt   niquests.authr   ImportErrorrequests.authcaldav.jmap._methods.calendarr   r   caldav.jmap._methods.eventr   r   r   r	   r
   r   r   r   r   caldav.jmap._methods.taskr   r   r   r   r   r   r   caldav.jmap.constantsr   r   r   caldav.jmap.convertr   caldav.jmap.errorr   r   caldav.jmap.objects.calendarr   #caldav.jmap.objects.calendar_objectr   caldav.jmap.sessionr   r    caldav.requestsr!   	getLoggerrq   rp   r   r"   r]   r4   r4   r4   r5   <module>   s4    
,$	
h