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
22 changes: 11 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ jobs:
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${ hashFiles('**/requirements.txt') }}
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
- name: Install flake8
run: |
python -m pip install --upgrade pip
pip install flake8
python -m pip install flake8
- name: Run flake8
run: |
flake8 || true
Expand All @@ -53,14 +53,14 @@ jobs:
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${ hashFiles('**/requirements.txt') }}
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
python -m pip install --upgrade pip setuptools wheel
python -m pip install -r requirements.txt
- name: Run unit tests with coverage
run: |
pytest --cov=app --cov-report=xml:coverage.xml --cov-report=term-missing tests/unit -q
python -m pytest --cov=app --cov-report=xml:coverage.xml --cov-report=term-missing tests/unit -q
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
Expand Down Expand Up @@ -99,8 +99,8 @@ jobs:
file: Dockerfile.prod
push: true
tags: |
ghcr.io/${ github.repository_owner }}/${ github.repository }}:latest
ghcr.io/${ github.repository_owner }}/${ github.repository }}:${ github.sha }}
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Optionally push to Docker Hub
if: ${{ env.DOCKERHUB_USERNAME && env.DOCKERHUB_TOKEN }}
uses: docker/build-push-action@v4
Expand All @@ -109,13 +109,13 @@ jobs:
file: Dockerfile.prod
push: true
tags: |
${{ env.DOCKERHUB_USERNAME }}/${ github.repository }}:latest
${{ env.DOCKERHUB_USERNAME }}/${ github.repository }}:${ github.sha }}
${{ env.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:latest
${{ env.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ github.sha }}
- name: Create release (optional)
if: github.event_name == 'workflow_dispatch'
uses: ncipollo/release-action@v1
with:
tag: v${ github.run_number }}
tag: v${{ github.run_number }}
name: "Automated release ${{ github.run_number }}"
body: |
Automated release from CI build ${{ github.run_id }}
Expand Down
46 changes: 36 additions & 10 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,24 @@

from notion_client import Client

NOTION_TOKEN = os.getenv("NOTION_TOKEN")
if not NOTION_TOKEN:
raise RuntimeError("NOTION_TOKEN not found in environment. Please set it in .env or env vars")
# Defer client creation until it's actually needed. Importing the module
# doesn't require configured credentials which keeps startup side-effect
# free and makes testing easier.
notion: Optional[Client] = None

notion = Client(auth=NOTION_TOKEN)
def _get_client() -> Client:
"""Return the configured Notion client or raise an HTTP error."""
global notion
if notion is not None:
return notion
token = os.getenv("NOTION_TOKEN")
if token is None:
raise HTTPException(
status_code=500,
detail="Notion client not configured – set NOTION_TOKEN",
)
notion = Client(auth=token)
return notion

app = FastAPI(title="Notion API for ChatGPT Actions",
description="A lightweight wrapper around Notion API for ChatGPT Actions and OpenWebUI tools",
Expand Down Expand Up @@ -42,49 +55,62 @@ def health():
@app.get("/v1/databases")
async def list_databases():
try:
resp = notion.search(filter={"property": "object", "value": "database"})
client = _get_client()
resp = client.search(filter={"property": "object", "value": "database"})
return resp
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

@app.get("/v1/databases/{database_id}/rows")
async def list_database_rows(database_id: str = Path(..., description="Notion Database ID"),
page_size: int = 50):
try:
resp = notion.databases.query(database_id=database_id, page_size=page_size)
client = _get_client()
resp = client.databases.query(database_id=database_id, page_size=page_size)
return resp
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

@app.get("/v1/pages/{page_id}")
async def get_page(page_id: str = Path(..., description="Notion Page ID")):
try:
page = notion.pages.retrieve(page_id=page_id)
blocks = notion.blocks.children.list(block_id=page_id)
client = _get_client()
page = client.pages.retrieve(page_id=page_id)
blocks = client.blocks.children.list(block_id=page_id)
return {"page": page, "blocks": blocks}
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

@app.post("/v1/pages")
async def create_page(req: CreatePageRequest):
try:
client = _get_client()
payload = {
"parent": {"database_id": req.parent_database_id} if req.parent_database_id else {"type": "page_id"},
"properties": req.properties
}
if req.children:
payload["children"] = req.children
resp = notion.pages.create(**payload)
resp = client.pages.create(**payload)
return resp
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

@app.patch("/v1/pages/{page_id}")
async def update_page(page_id: str, req: UpdatePageRequest):
try:
client = _get_client()
if not req.properties:
raise HTTPException(status_code=400, detail="No properties provided")
resp = notion.pages.update(page_id=page_id, properties=req.properties)
resp = client.pages.update(page_id=page_id, properties=req.properties)
return resp
except HTTPException:
raise
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ fastapi==0.95.2
uvicorn[standard]==0.21.1
python-dotenv==1.0.0
notion-client==0.6.0
pydantic==1.10.10
pydantic==1.10.22
httpx==0.24.1
pytest==7.4.0
pytest-asyncio==0.21.0
Expand Down