Source code for pyshortio.link_queries

# -*- coding: utf-8 -*-

"""
Short.io Link query API implementation.

This module provides classes and methods for interacting with the Short.io Link-related
API endpoints. It focuses on query operations that retrieve link information from the
Short.io service, implementing a comprehensive suite of methods for different query
patterns.

he implementation follows three key design patterns:

1. **Return Pattern**:

All methods representing Short.io API calls return a tuple of two objects:

- First: The raw `requests.Response` object, allowing users complete access to
 HTTP response details (headers, status codes, etc.)
- Second: A method-specific result in a Pythonic format (e.g., a list of Link objects)

Each method includes a `raise_for_status` parameter that controls whether
exceptions are raised immediately on HTTP errors, giving users fine-grained control
over error handling.

2. **Parameter Handling**:

Methods use the NA (Not Applicable) sentinel value for optional parameters,
which are filtered out before being sent to the API using the `rm_na` utility.

3. **Pagination Abstraction Pattern**:

Short.io API list methods use next page tokens for pagination. This module implements
a universal pagination mechanism that converts any regular API method to an
auto-paginating method. These methods are prefixed with `pagi_` followed by the
original method name and return an iterable of the original method's return values
(typically tuples of response and specialized objects).
"""

import typing as T
from datetime import datetime

from requests import Response

from .arg import NA, rm_na
from .constants import DEFAULT_RAISE_FOR_STATUS
from .model import Link, Folder
from .paginator import _paginate


if T.TYPE_CHECKING:  # pragma: no cover
    from .client import Client


[docs] class LinkQueriesMixin: """ Mixin class providing Link-related query methods for the Client. This class implements various methods for retrieving link information from the Short.io API. It focuses exclusively on query operations (retrieving existing links) rather than modification operations, which are handled by the LinkManagementMixin class. All methods follow the Dual Return Pattern, returning both the raw HTTP response and a Pythonic representation of the API result. Methods prefixed with ``pagi_`` implement the Pagination Abstraction Pattern, automatically handling pagination for list operations. These methods interact with Link objects which are defined in the model module, providing a clean separation between data models and API operations. """ def list_folders( self: "Client", domain_id: int, raise_for_status: bool = DEFAULT_RAISE_FOR_STATUS, ) -> tuple[Response, list[Folder]]: """ This method retrieves all folders used to organize links for a domain. Example: >>> # List all folders for a domain >>> response, folder_list = client.list_folders( ... domain_id=45678 ... ) >>> # Print folder information >>> for folder in folder_list: ... print(f"ID: {folder.id}, Name: {folder.name}") Ref: - https://developers.short.io/reference/get_links-folders-domainid """ url = f"{self.endpoint}/links/folders/{domain_id}" response = self.http_get(url=url) if raise_for_status: response.raise_for_status() if response.status_code == 200: folder_list = [ Folder(_data=dct) for dct in response.json().get("linkFolders", []) ] else: raise NotImplementedError("Unexpected response code") return response, folder_list
[docs] def get_folder( self: "Client", domain_id: int, folder_id: str, raise_for_status: bool = DEFAULT_RAISE_FOR_STATUS, ) -> tuple[Response, T.Optional[Folder]]: """ This method retrieves detailed information about a specific folder. Example: >>> # Get folder information >>> response, folder = client.get_folder( ... domain_id=45678, ... folder_id="fld_abc123def456" ... ) >>> if folder: ... print(f"Folder name: {folder.name}") >>> >>> # Try to get a non-existent folder >>> response, folder = client.get_folder( ... domain_id=45678, ... folder_id="non_existent", ... raise_for_status=False ... ) >>> print(folder) # Output: None Ref: - https://developers.short.io/reference/get_links-folders-domainid-folderid """ url = f"{self.endpoint}/links/folders/{domain_id}/{folder_id}" response = self.http_get(url=url) if raise_for_status: # pragma: no cover response.raise_for_status() if response.status_code == 200: response_json = response.json() if response_json is None: folder = None else: folder = Folder(_data=response.json()) elif response.status_code == 404: # pragma: no cover folder = None else: # pragma: no cover raise NotImplementedError("Unexpected response code") return response, folder
[docs] def list_folders( self: "Client", domain_id: int, raise_for_status: bool = DEFAULT_RAISE_FOR_STATUS, ) -> tuple[Response, list[Folder]]: """ This method retrieves all folders used to organize links for a domain. Example: >>> # List all folders for a domain >>> response, folder_list = client.list_folders( ... domain_id=45678 ... ) >>> # Print folder information >>> for folder in folder_list: ... print(f"ID: {folder.id}, Name: {folder.name}") Ref: - https://developers.short.io/reference/get_links-folders-domainid """ url = f"{self.endpoint}/links/folders/{domain_id}" response = self.http_get(url=url) if raise_for_status: response.raise_for_status() if response.status_code == 200: folder_list = [ Folder(_data=dct) for dct in response.json().get("linkFolders", []) ] else: raise NotImplementedError("Unexpected response code") return response, folder_list
[docs] def create_folder( self: "Client", domain_id: int, name: str, color: str = NA, background_color: str = NA, logo_url: str = NA, logo_height: str = NA, logo_width: str = NA, ec_level: str = NA, integration_fb: str = NA, integration_ga: str = NA, integration_gtm: str = NA, integration_adroll: str = NA, utm_campaign: str = NA, utm_medium: str = NA, utm_source: str = NA, redirect_type: int = NA, expires_at_days: int = NA, icon: str = NA, prefix: str = NA, raise_for_status: bool = DEFAULT_RAISE_FOR_STATUS, ) -> tuple[Response, T.Optional[Folder]]: """ Create a new folder for organizing links. Ref: - https://developers.short.io/reference/post_links-folders """ url = f"{self.endpoint}/links/folders" data = { "domainId": domain_id, "name": name, "color": color, "backgroundColor": background_color, "logoUrl": logo_url, "logoHeight": logo_height, "logoWidth": logo_width, "ecLevel": ec_level, "integrationFB": integration_fb, "integrationGA": integration_ga, "integrationGTM": integration_gtm, "integrationAdroll": integration_adroll, "utmCampaign": utm_campaign, "utmMedium": utm_medium, "utmSource": utm_source, "redirectType": redirect_type, "expiresAtDays": expires_at_days, "icon": icon, "prefix": prefix, } data = rm_na(**data) response = self.http_post(url=url, data=data) if raise_for_status: # pragma: no cover response.raise_for_status() if response.status_code in [200, 201]: folder = Folder(_data=response.json()) else: # pragma: no cover raise NotImplementedError("Unexpected response code") return response, folder