DomoLibrary Classes Implementation Guidelines

This document outlines best practices for designing and implementing classes in DomoLibrary. It is divided into several sections to guide you through naming conventions, structure, integration with API route functions, error handling, and overall code style.


1. Overview

DomoLibrary classes encapsulate functionality for different domains (e.g. accounts, jobs, enterprise apps). The guidelines below ensure consistency, maintainability, and clarity across the codebase.


2. Class Naming and Organization

  • Naming: Use CamelCase for all class names.
  • Organization: Group classes by functionality (such as account classes, job classes, etc.) and store them in their respective modules.

3. Class Structure and Inheritance

  • Docstrings: Begin every class with a clear docstring explaining its purpose and main attributes.
  • Type Annotations: Use complete type annotations for all attributes and methods.
  • Inheritance: Use inheritance only when it adds clarity or reusability; prefer composition when possible.
  • Initialization: Keep your __init__ or __post_init__ methods focused on setting up state.

4. Integration with Route Functions

Classes that interact with Domo API resources must wrap API calls (handled by route functions) inside their asynchronous methods.

  • Asynchronous Calls: Always use await when invoking route functions.

  • Response Validation: Check res.is_success after each API call. If the check fails, raise a custom exception.

  • Example:

    @classmethod
    async def from_id(cls, auth: dmda.DomoAuth, entity_id: str) -> "MyEntity":
        """
        Retrieves an entity using a route function.
    
        Args:
            auth (dmda.DomoAuth): The authentication object.
            entity_id (str): Unique identifier for the entity.
    
        Returns:
            MyEntity: An instance loaded with API response data.
    
        Raises:
            SomeAPIError: If the API call fails.
        """
        res = await get_entity_by_id(auth=auth, entity_id=entity_id)  # Route function call
        if res.is_success:
            instance = cls(auth=auth, entity_id=entity_id)
            instance.data = res.response
            return instance
        else:
            raise SomeAPIError("Failed to retrieve entity")

5. Error Handling and Type Annotations

  • Response Checks: Always verify API responses (e.g., using res.is_success) and handle errors by raising appropriate custom exceptions.
  • Comprehensive Docstrings: Each function/method should include a docstring that details purpose, parameters, return types, and documented exceptions.
  • Type Hints: Specify types for all parameters and return values to improve clarity and catch potential errors earlier.

6. Code Readability and Maintainability

  • PEP8 Compliance:
    • Use 4 spaces per indentation level.
    • Limit lines to 79 characters.
    • Prefer descriptive variable names.
  • Conciseness: Keep methods focused on a single responsibility. Break down complex logic into smaller helper functions.
  • Separation of Concerns: Distinguish between API communication (handled by route functions) and business logic.

7. Expanded Python Style Guide

7.1 Code Organization

  • Structure your code into modular, small, single-purpose functions.
  • Separate API call logic (route functions) from data processing and business logic.

7.2 Comprehensive Documentation

  • Every function and method must have a detailed docstring describing:
    • Its purpose.
    • Input parameters and their types.
    • Return values and their types.
    • Any exceptions that could be raised.
  • Use inline comments for non-obvious logic.

7.3 Error Handling Best Practices

  • Always validate API responses using conditions like if not res.is_success:.
  • Raise exceptions with clear, informative messages that include context such as the Domo instance and function name.
  • Use try/except blocks judiciously to manage recoverable errors while allowing unexpected ones to bubble up.

8. Additional Guidance for New Contributors

  • Getting Started: Familiarize yourself with this document along with the provided code examples.
  • Adopt Best Practices: Use tools like linters (e.g., pylint, flake8) and formatters (e.g., Black) to ensure your code meets the standards.
  • Documentation Updates: Keep documentation synchronized with code changes.
  • Ask for Help: If any guideline is unclear, reach out to project maintainers.

9. Common Class Methods and Subclassing

  • Single Entity Classes:
    Classes that represent a single Domo entity (e.g., DomoUser, DomoAccount) are typically defined using a singular name and include a classmethod get_by_id to retrieve an instance via an API call.

  • CRUD Operations:
    Common methods provided by these classes include:

    • create for instantiating new entities,
    • update for modifying existing entities, and
    • delete for removing entities.
  • Grouped (Plural) Classes:
    For managing collections of entities, classes use plural names (e.g., DomoDatasets, DomoAppStudios). These classes usually implement:

    • A get() method to retrieve all entities,
    • Additional search functionality, and
    • An upsert() method to update or insert entities based on criteria.
  • Subclassing for Related Entities:
    Some entities are complex and include nested or related data managed in subclasses. For instance:

    • Domo Datasets are often accompanied by a corresponding Schema class (e.g., DomoDataset_Schema), and
    • DomoAccounts make use of various AccountConfig classes to manage authentication and configuration details.

    In these cases, subclass attributes are typically denoted with a capital letter (e.g., Lineage, Config), while class-level attributes remain in lowercase.

  • Reference Implementations:

    • The DomoAccount class implements a get_by_id method and delegates configuration management to its various AccountConfig subclasses.
    • The DomoDataset class works in tandem with DomoDataset_Schema to manage schema details, and its plural counterpart, DomoDatasets, provides methods for searching and upserting datasets.