Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#################
# ENVIRONMENTAL #
#################
PPP_HTTP_PROXY=
PPP_HTTPS_PROXY=
HTTP_PROXY=
HTTPS_PROXY=
VERIFY_SSL=

##############
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ jobs:
TWILIO_API_SID: dummy
TWILIO_API_SECRET: dummy
URLSCAN_API_KEY: dummy
PYTHONPATH: .
run: poetry run pytest --ignore=dev_env
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# ppp_connectors
# APIary

A clean, modular set of Python connectors and utilities for working with both **APIs** and **DBMS backends**, unified by a centralized `Broker` abstraction and a consistent interface. Designed for easy testing, code reuse, and plug-and-play extensibility.

Expand Down Expand Up @@ -76,7 +76,7 @@ Each API connector has both a sync and async version (e.g., `URLScanConnector` a

### 🌐 Sync Example (URLScan)
``` python
from ppp_connectors.api_connectors.urlscan import URLScanConnector
from apiary.api_connectors.urlscan import URLScanConnector

scanner = URLScanConnector(load_env_vars=True)
result = scanner.scan(url="https://example.com")
Expand All @@ -87,7 +87,7 @@ print(result.json())
### ⚡ Async Example (URLScan)
``` python
import asyncio
from ppp_connectors.api_connectors.urlscan import AsyncURLScanConnector
from apiary.api_connectors.urlscan import AsyncURLScanConnector

async def main():
scanner = AsyncURLScanConnector(load_env_vars=True)
Expand Down Expand Up @@ -135,7 +135,7 @@ API connectors inherit from the `Broker` class and support flexible proxy config

*Using a single proxy:*
```python
from ppp_connectors.api_connectors.urlscan import URLScanConnector
from apiary.api_connectors.urlscan import URLScanConnector
conn = URLScanConnector(proxy="http://myproxy:8080")
```

Expand Down Expand Up @@ -196,7 +196,7 @@ Note: `query(...)` is deprecated in favor of `find(filter=..., projection=..., b

Sync connector
```python
from ppp_connectors.dbms_connectors.mongo import MongoConnector
from apiary.dbms_connectors.mongo import MongoConnector

# Recommended: use as a context manager (auto-closes)
with MongoConnector(
Expand Down Expand Up @@ -237,7 +237,7 @@ finally:
Async connector
```python
import asyncio
from ppp_connectors.dbms_connectors.mongo_async import AsyncMongoConnector
from apiary.dbms_connectors.mongo_async import AsyncMongoConnector

async def main():
async with AsyncMongoConnector(
Expand Down Expand Up @@ -266,7 +266,7 @@ asyncio.run(main())

```python
# The query method returns a generator; use list() or iterate to access results
from ppp_connectors.dbms_connectors.elasticsearch import ElasticsearchConnector
from apiary.dbms_connectors.elasticsearch import ElasticsearchConnector

conn = ElasticsearchConnector(["http://localhost:9200"])
results = list(conn.query("my-index", {"query": {"match_all": {}}}))
Expand All @@ -279,7 +279,7 @@ for doc in results:
For automatic connection handling, use `ODBCConnector` as a context manager

```python
from ppp_connectors.dbms_connectors.odbc import ODBCConnector
from apiary.dbms_connectors.odbc import ODBCConnector

with ODBCConnector("DSN=PostgresLocal;UID=postgres;PWD=postgres") as db:
rows = conn.query("SELECT * FROM my_table")
Expand All @@ -289,7 +289,7 @@ with ODBCConnector("DSN=PostgresLocal;UID=postgres;PWD=postgres") as db:
If you'd like to keep manual control, you can still use the `.close()` method

```python
from ppp_connectors.dbms_connectors.odbc import ODBCConnector
from apiary.dbms_connectors.odbc import ODBCConnector

conn = ODBCConnector("DSN=PostgresLocal;UID=postgres;PWD=postgres")
rows = conn.query("SELECT * FROM my_table")
Expand All @@ -300,7 +300,7 @@ conn.close()
### Splunk

```python
from ppp_connectors.dbms_connectors.splunk import SplunkConnector
from apiary.dbms_connectors.splunk import SplunkConnector

conn = SplunkConnector("localhost", 8089, "admin", "admin123", scheme="https", verify=False)
results = conn.query("search index=_internal | head 5")
Expand Down Expand Up @@ -344,8 +344,8 @@ markers =
To add a new connector:

1. **Module**: Place your module in:
- `ppp_connectors/api_connectors/` for API-based integrations
- `ppp_connectors/dbms_connectors/` for database-style connectors
- `src/apiary/api_connectors/` for API-based integrations
- `src/apiary/dbms_connectors/` for database-style connectors

2. **Base class**:
- Use the `Broker` class for APIs
Expand All @@ -371,8 +371,8 @@ To add a new connector:
## 🛠️ Dev Environment

```bash
git clone https://github.com/FineYoungCannibals/ppp_connectors.git
cd ppp_connectors
git clone https://github.com/robd518/apiary.git
cd apiary

cp .env.example .env

Expand Down
57 changes: 57 additions & 0 deletions dev_env/api/test_async_generic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import asyncio
from typing import Any, Dict, Iterable, Tuple

from apiary.api_connectors.generic import AsyncGenericConnector

# Give AsyncGenericConnector lightweight context-manager support without
# modifying the library source.
if not hasattr(AsyncGenericConnector, "__aenter__"):
async def _async_enter(self: AsyncGenericConnector) -> AsyncGenericConnector:
return self

async def _async_exit(
self: AsyncGenericConnector,
exc_type,
exc,
tb,
) -> None:
await self.session.aclose()

AsyncGenericConnector.__aenter__ = _async_enter # type: ignore[attr-defined]
AsyncGenericConnector.__aexit__ = _async_exit # type: ignore[attr-defined]

RequestSpec = Tuple[str, str, Dict[str, Any]]

REQUESTS: Iterable[RequestSpec] = (
("GET", "/get", {"params": {"label": "alpha"}}),
("GET", "/delay/1", {"params": {"label": "beta"}}),
("GET", "/uuid", {"headers": {"X-Demo": "gamma"}}),
)


async def fetch(connector: AsyncGenericConnector, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
response = await connector.request(method, endpoint, **kwargs)
return {
"endpoint": endpoint,
"status": response.status_code,
"json": response.json(),
}


async def main() -> None:
async with AsyncGenericConnector(
base_url="http://httpbin.org",
enable_logging=False,
timeout=15,
) as connector:
tasks = [fetch(connector, method, endpoint, **req_kwargs) for method, endpoint, req_kwargs in REQUESTS]
results = await asyncio.gather(*tasks)

print("Completed concurrent requests:")
for result in results:
print(f"- {result['endpoint']} -> {result['status']}")
print(f" payload: {result['json']}")


if __name__ == "__main__":
asyncio.run(main())
2 changes: 1 addition & 1 deletion dev_env/api/test_flashpoint.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from ppp_connectors.api_connectors.flashpoint import FlashpointConnector
from apiary.api_connectors.flashpoint import FlashpointConnector
import httpx

fp = FlashpointConnector(
Expand Down
28 changes: 28 additions & 0 deletions dev_env/api/test_ipqs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from apiary.api_connectors.ipqs import AsyncIPQSConnector, IPQSConnector
import asyncio
from apiary.helpers import combine_env_configs, setup_logger
from typing import Dict, Any
import json


env_config: Dict[str, Any] = combine_env_configs()

async def main():
ipqs = AsyncIPQSConnector(
api_key=env_config["IPQS_API_KEY"],
enable_logging=True,
load_env_vars=True,
trust_env=True,
verify=False
)

res = await ipqs.malicious_url(
query="cracked.to"
)

print(res)
print(res.json())
print(res.headers)

asyncio.run(main())
# main()
2 changes: 1 addition & 1 deletion dev_env/api/test_urlscan.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from ppp_connectors.api_connectors.urlscan import URLScanConnector, AsyncURLScanConnector
from apiary.api_connectors.urlscan import URLScanConnector, AsyncURLScanConnector

urlscan = URLScanConnector(
load_env_vars=True,
Expand Down
4 changes: 2 additions & 2 deletions dev_env/elasticsearch/test_es.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from ppp_connectors.dbms_connectors.elasticsearch import ElasticsearchConnector
from ppp_connectors.helpers import combine_env_configs, setup_logger
from apiary.dbms_connectors.elasticsearch import ElasticsearchConnector
from apiary.helpers import combine_env_configs, setup_logger
from typing import Dict, Any
import json

Expand Down
4 changes: 2 additions & 2 deletions dev_env/example.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from ppp_connectors.dbms_connectors.mongo import MongoConnector
from ppp_connectors.helpers import combine_env_configs
from apiary.dbms_connectors.mongo import MongoConnector
from apiary.helpers import combine_env_configs
from typing import Dict, Any

env_config: Dict[str, Any] = combine_env_configs()
Expand Down
4 changes: 2 additions & 2 deletions dev_env/mongo/test_mongo.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Dict, Any, List
from ppp_connectors.dbms_connectors.mongo import MongoConnector
from ppp_connectors.helpers import combine_env_configs, setup_logger
from apiary.dbms_connectors.mongo import MongoConnector
from apiary.helpers import combine_env_configs, setup_logger


def main() -> None:
Expand Down
4 changes: 2 additions & 2 deletions dev_env/mongo/test_mongo_async.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import asyncio
from typing import Any, Dict, List

from ppp_connectors.dbms_connectors.mongo_async import AsyncMongoConnector
from ppp_connectors.helpers import combine_env_configs, setup_logger
from apiary.dbms_connectors.mongo_async import AsyncMongoConnector
from apiary.helpers import combine_env_configs, setup_logger


async def main() -> None:
Expand Down
4 changes: 2 additions & 2 deletions dev_env/odbc/test_odbc.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from ppp_connectors.dbms_connectors.odbc import ODBCConnector
from ppp_connectors.helpers import combine_env_configs, setup_logger
from apiary.dbms_connectors.odbc import ODBCConnector
from apiary.helpers import combine_env_configs, setup_logger
from typing import Dict, Any

env_config: Dict[str, Any] = combine_env_configs()
Expand Down
4 changes: 2 additions & 2 deletions dev_env/splunk/test_splunk.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from ppp_connectors.dbms_connectors.splunk import SplunkConnector
from ppp_connectors.helpers import combine_env_configs, setup_logger
from apiary.dbms_connectors.splunk import SplunkConnector
from apiary.helpers import combine_env_configs, setup_logger
from typing import Dict, Any

env_config: Dict[str, Any] = combine_env_configs()
Expand Down
4 changes: 2 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 0 additions & 43 deletions ppp_connectors/__init__.py

This file was deleted.

14 changes: 0 additions & 14 deletions ppp_connectors/broker.py

This file was deleted.

10 changes: 6 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
[tool.poetry]
name = "ppp-connectors"
packages = [{ include = "ppp_connectors" }]
version = "1.1.13"
name = "apiary"
packages = [{ include = "apiary", from = "src" }]
version = "2.0.0"
description = "A simple, lightweight set of connectors and functions to various APIs and DBMSs, controlled by a central broker."
authors = ["Rob D'Aveta <rob.daveta@gmail.com>"]
readme = "README.md"
homepage = "https://github.com/robd518/apiary"
repository = "https://github.com/robd518/apiary"

[tool.poetry.dependencies]
python = "^3.10"
python-dotenv = "^1.1.1"
pymongo = "^4.15.0"
elasticsearch = "^9.1.1"
splunk-sdk = "^2.1.1"
pyodbc = { version = "^5.2.0", optional = true }
httpx = "^0.28.1"
tenacity = "^9.1.2"
pyodbc = "^5.3.0"

[tool.poetry.extras]
odbc = ["pyodbc"]
Expand Down
Loading