aperture-py 2.26.0
SDK to interact with the Aperture Agent
Requires Python: >=3.8,<4.0

<p align="center">
🚀 <b></b>Just launched v1 of <a href="https://blog.fluxninja.com/blog/aperture-v1-launch-2024/">managed rate limiting service</a></b>
  <br/>Support us with your feedback and questions on <a href="https://www.producthunt.com/posts/fluxninja-aperture">Product Hunt</a> or <a href="https://discord.gg/U3N3fCZEPm">Discord</a>
  <br/>
</p>

<p align="center">
  <img src="https://github.com/fluxninja/aperture/raw/main/docs/content/assets/img/aperture_logo.png" alt="FluxNinja Aperture" width="75%">
  <br/>
  <a href="https://docs.fluxninja.com">
    <img alt="Documentation Reference" src="https://img.shields.io/badge/documentation-reference-brightgreen?style=for-the-badge&logo=data:image/png;base64,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">
  </a>
  <a href="https://join.slack.com/t/fluxninja-aperture/shared_invite/zt-1eunlrkhh-10P1HUkmBfVJX3qrSLRk~g">
    <img alt="Slack Community" src="https://img.shields.io/badge/Join%20Our%20Community-Slack-brightgreen?style=for-the-badge&logo=slack">
  </a>
</p>

# Rate Limiting for Python Applications

The `aperture-py` SDK provides an easy way to integrate your Python applications
with [FluxNinja Aperture](https://github.com/fluxninja/aperture). It allows flow
control functionality on fine-grained features inside service code.

Refer to [documentation](https://docs.fluxninja.com/sdk/python/) for more
details.

## Usage

### Install SDK

Run the command below to install the SDK:

```bash
pip install aperture-py
```

### Create Aperture Client

The next step is to create an Aperture Client instance, for which, the address
of the organization created in Aperture Cloud and API key are needed. You can
locate both these details by clicking on the Aperture tab in the sidebar menu of
Aperture Cloud.

```python
from aperture_sdk.client import ApertureClient, FlowParams

agent_address = os.getenv("APERTURE_AGENT_ADDRESS", default_agent_address)
api_key = os.getenv("APERTURE_API_KEY", "")
insecure = os.getenv("APERTURE_AGENT_INSECURE", "true").lower() == "true"

aperture_client = ApertureClient.new_client(
    address=agent_address, insecure=insecure, api_key=api_key
)
```

### Flow Functionality

The created instance can then be used to start a flow:

```python
# business logic produces labels
    labels = {
        "user_id": "some_user_id",
        "user_tier": "gold",
        "priority": "100",
    }
    flow_params = FlowParams(
        check_timeout=timedelta(seconds=200),
        explicit_labels=labels,
    )
    # start_flow performs a flowcontrol.v1.Check call to Aperture Agent.
    # It returns a Flow or raises an error if any.
    flow = await aperture_client.start_flow(
        control_point="AwesomeFeature",
        params=flow_params,
    )

    # Check if flow check was successful.
    if not flow.success:
        logger.info("Flow check failed - will fail-open")

    # See whether flow was accepted by Aperture Agent.
    if flow.should_run():
        # do actual work
        pass
    else:
        # handle flow rejection by Aperture Agent
        flow.set_status(FlowStatus.Error)

    res = await flow.end()
    if res.get_error():
        logger.error("Error: {}".format(res.get_error()))
    elif res.get_flow_end_response():
        logger.info("Flow End Response: {}".format(res.get_flow_end_response()))

    # Simulate work being done
    await asyncio.sleep(2)
    return "", 202
```

The above code snippet is making `start_flow` calls to Aperture. For this call,
it is important to specify the control point (`AwesomeFeature` in the example)
and `FlowParams` that will be aligned with the policy created in Aperture Cloud.
For request prioritization use cases, it's important to set a higher gRPC
deadline. This parameter specifies the maximum duration a request can remain in
the queue. For each flow that is started, a `should_run` decision is made,
determining whether to allow the request into the system or to rate limit it. It
is important to make the `end` call made after processing each request, to send
telemetry data that would provide granular visibility for each flow.