Metadata-Version: 2.4
Name: tiferet-flask
Version: 0.2.1
Summary: An extension of the Tiferet Framework for the Flask API.
Author-email: "Andrew Shatz, Great Strength Systems" <andrew@greatstrength.me>
License: MIT
Project-URL: Homepage, https://github.com/greatstrength/tiferet-flask
Project-URL: Repository, https://github.com/greatstrength/tiferet-flask
Project-URL: Download, https://github.com/greatstrength/tiferet-flask
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: tiferet>=2.0.0a8
Requires-Dist: flask>=3.1.2
Requires-Dist: flask_cors>=6.0.1
Provides-Extra: test
Requires-Dist: pytest>=8.3.3; extra == "test"
Requires-Dist: pytest_env>=1.1.5; extra == "test"
Dynamic: license-file

# Tiferet Flask - A Flask Extension for the Tiferet Framework

## Introduction

Tiferet Flask elevates the Tiferet Python framework by empowering developers to craft sophisticated Flask-based APIs rooted in Domain-Driven Design (DDD) principles. Drawing from the Kabbalistic ideal of beauty in harmony, Tiferet Flask seamlessly blends Tiferet's domain event-driven architecture with Flask's intuitive routing and request handling. The result is a powerful, modular platform for building scalable web services that distill complex business logic into elegant, extensible API designs.

This tutorial walks you through creating a streamlined calculator API, leveraging Tiferet's robust domain events and configurations while introducing Flask-specific interfaces and endpoints. For a deeper understanding of Tiferet's foundational concepts, consult the [Tiferet documentation](https://github.com/greatstrength/tiferet).

## Getting Started with Tiferet Flask

Embark on your Tiferet Flask journey by preparing your development environment. This guide assumes familiarity with Tiferet's core setup.

### Installing Python

Tiferet Flask requires Python 3.10 or later. Follow the detailed [Python installation instructions](https://github.com/greatstrength/tiferet?tab=readme-ov-file#installing-python) in the Tiferet README for your platform (Windows, macOS, or Linux). Verify your installation with:

```bash
python3.10 --version
```

### Setting Up a Virtual Environment

Create a dedicated virtual environment named `tiferet_flask_app` to keep dependencies organized:

```bash
# Create the Environment
# Windows
python -m venv tiferet_flask_app

# macOS/Linux
python3.10 -m venv tiferet_flask_app

# Activate the Environment
# Windows (Command Prompt)
tiferet_flask_app\Scripts\activate

# Windows (PowerShell)
.\tiferet_flask_app\Scripts\Activate.ps1

# macOS/Linux
source tiferet_flask_app/bin/activate
```

Exit the environment with `deactivate` when finished.

## Your First Calculator API

With your environment ready, install dependencies and configure the project structure to build a dynamic calculator API using Tiferet Flask.

### Installing Tiferet Flask

In your activated virtual environment, install the Tiferet Flask extension using pip:

```bash
# Windows
pip install tiferet_flask

# macOS/Linux
pip3 install tiferet_flask
```

Note: If developing locally, replace with the appropriate local installation command.

### Project Structure

Adapt Tiferet's project structure to incorporate Flask, adding a dedicated API script:

```plaintext
project_root/
├── basic_calc.py
├── calc_cli.py
├── calc_flask_api.py
├── app/
    ├── events/
    │   ├── __init__.py
    │   ├── calc.py
    │   └── settings.py
    └── configs/
        ├── __init__.py
        ├── app.yml
        ├── container.yml
        ├── error.yml
        ├── feature.yml
        ├── flask.yml
        └── logging.yml
```

The `app/events/` and `app/configs/` directories align with Tiferet's structure (see [Tiferet README](https://github.com/greatstrength/tiferet?tab=readme-ov-file#project-structure)). The `calc_flask_api.py` script initializes and runs the Flask API, while `flask.yml` defines blueprint and routing configurations.

## Crafting the Calculator API

Extend Tiferet's calculator application into a powerful API by reusing its domain events and configurations, enhanced with Flask-specific functionality.

### Defining Base and Arithmetic Domain Event Classes

Leverage Tiferet's `BasicCalcEvent` (`app/events/settings.py`) for input validation and arithmetic domain events (`AddNumber`, `SubtractNumber`, `MultiplyNumber`, `DivideNumber`, `ExponentiateNumber` in `app/events/calc.py`) for core operations. These remain unchanged from the original calculator app; refer to the [Tiferet README](https://github.com/greatstrength/tiferet?tab=readme-ov-file#defining-base-and-arithmetic-domain-event-classes) for details.

### Configuring the Calculator API

Reuse Tiferet's `container.yml` ([here](https://github.com/greatstrength/tiferet?tab=readme-ov-file#configuring-the-container-in-configscontaineryml)), `error.yml` ([here](https://github.com/greatstrength/tiferet?tab=readme-ov-file#configuring-the-errors-in-configserroryml)), and `feature.yml` ([here](https://github.com/greatstrength/tiferet?tab=readme-ov-file#configuring-the-features-in-configsfeatureyml)) for domain event mappings, error handling, and feature workflows. Introduce a Flask-specific interface in `app.yml` and routing configurations in `flask.yml`.

#### Configuring the App Interface in `configs/app.yml`

Enhance `app/configs/app.yml` with the `calc_flask_api` interface:

```yaml
interfaces:
  calc_flask_api:
    name: Basic Calculator API
    description: Perform basic calculator operations via Flask API
    module_path: tiferet_flask.contexts.flask
    class_name: FlaskApiContext
    attrs:
      get_blueprints_handler:
        module_path: tiferet_flask.events.flask
        class_name: GetFlaskBlueprints
      get_route_handler:
        module_path: tiferet_flask.events.flask
        class_name: GetFlaskRoute
      get_status_code_handler:
        module_path: tiferet_flask.events.flask
        class_name: GetFlaskStatusCode
      flask_service:
        module_path: tiferet_flask.repos.flask
        class_name: FlaskYamlRepository
        params:
          flask_config_file: app/configs/flask.yml
```

#### Configuring Blueprints, Routes, and Error Status Codes in `configs/flask.yml`

Define Flask API blueprints, routes, and error mappings in `app/configs/flask.yml`:

```yaml
flask:
  blueprints:
    calc:
      name: calc
      url_prefix: /calc
      routes:
        add:
          rule: /add
          methods: [POST, GET]
          status_code: 200
        subtract:
          rule: /subtract
          methods: [POST, GET]
        multiply:
          rule: /multiply
          methods: [POST, GET]
        divide:
          rule: /divide
          methods: [POST, GET]
        sqrt:
          rule: /sqrt
          methods: [POST, GET]
  errors:
    DIVIDE_BY_ZERO: 400 
```

The `url_prefix` ensures all routes are prefixed with `/calc`. Each route’s endpoint (e.g., `calc.add`) aligns with the corresponding feature ID in `feature.yml`. Routes specify a `rule`, supported HTTP `methods`, and a default `status_code` of 200. The `errors` section maps error codes from `error.yml` to HTTP status codes, ensuring proper error handling.

This configuration enables `FlaskApiContext` to orchestrate Flask API operations seamlessly.

### Initializing and Demonstrating the API in calc_flask_api.py

Create `calc_flask_api.py` to initialize the API and define endpoints:

```python
# *** imports

from tiferet import App
from tiferet_flask import FlaskApiContext

# *** functions

# * functions: view_func
def view_func(**kwargs):
    '''
    Call the view function whenever a route endpoint is invoked.

    The FlaskApiContext instance is accessed via closure (``context``),
    so it does not need to be passed explicitly as a parameter.

    :param kwargs: Additional keyword arguments.
    :type kwargs: dict
    :return: The result of the view function.
    :rtype: Any
    '''

    # Get the Flask request context.
    from flask import request, jsonify

    # Format the request data from the json payload (if applicable),
    # the query parameters, and any route parameters passed via kwargs.
    data = dict(request.json) if request.is_json else {}
    data.update(dict(request.args))
    data.update(kwargs)

    # Format header data from the request headers.
    headers = dict(request.headers)

    # Execute the feature from the request endpoint.
    response, status_code = context.run(
        feature_id=request.endpoint,
        headers=headers,
        data=data,
        **kwargs,
    )

    # Return the response as JSON.
    return jsonify(response), status_code

# *** exec

# Create the Flask API context.
context: FlaskApiContext = App().load_interface('calc_flask_api')

# Build the Flask app.
context.build_flask_app(view_func=view_func)

# Define the flask_app for external use (e.g., for Flask CLI or WSGI servers).
def flask_app():
    '''
    Create and return the Flask app for testing.

    :return: The Flask app.
    :rtype: Flask
    '''

    return context.flask_app

# Run the Flask app if this script is executed directly.
if __name__ == '__main__':
    context.flask_app.run(host='127.0.0.1', port=5000, debug=True)
```

This script initializes the Tiferet application, loads the `calc_flask_api` interface, and dynamically handles RESTful endpoints for arithmetic operations.

### Demonstrating the Calculator API

Launch the API:

```bash
python3 calc_flask_api.py
```

Test endpoints using curl or tools like Postman:

```bash
# Add two numbers
curl -X POST http://127.0.0.1:5000/calc/add -H "Content-Type: application/json" -d '{"a": 1, "b": 2}'
# Output: 3

# Calculate square root
curl -X POST http://127.0.0.1:5000/calc/sqrt -H "Content-Type: application/json" -d '{"a": 16}'
# Output: 4.0

# Division by zero
curl -X POST http://127.0.0.1:5000/calc/divide -H "Content-Type: application/json" -d '{"a": 5, "b": 0}'
# Output: {"error_code": "DIVIDE_BY_ZERO", "text": "Cannot divide by zero"}
```

## Conclusion

Tiferet Flask empowers developers to craft elegant, modular Flask APIs within Tiferet's DDD framework, as showcased in this calculator tutorial. By seamlessly reusing Tiferet's domain events and configurations and introducing a Flask interface, you've built a scalable, intuitive API with minimal effort. Expand its capabilities by integrating authentication, advanced features like trigonometric operations, or combining with Tiferet's CLI or TUI contexts. Dive into the [Tiferet documentation](https://github.com/greatstrength/tiferet) for advanced DDD techniques, and experiment with `app/configs/` to customize your API, transforming complex web applications—whether monolithic or networked—into clear, purposeful solutions.
