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
77 changes: 77 additions & 0 deletions .github/workflows/integrationtest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: Integrationtest

on:
schedule:
- cron: '0 22 * * 1'
push:
branches: [ main ]
workflow_dispatch:
workflow_call:

jobs:

perses-api-sdk-test:
name: Integration tests
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ '3.10' ]

services:
perses:
image: persesdev/perses:v0.53.1
ports:
- 8080:8080
env:
PERSES_SECURITY_ENABLE_AUTH: "true"
PERSES_SECURITY_AUTHENTICATION_PROVIDERS_ENABLE_NATIVE: "true"
PERSES_SECURITY_AUTHORIZATION_PROVIDER_NATIVE_GUEST_PERMISSIONS_0_ACTIONS_0: "*"
PERSES_SECURITY_AUTHORIZATION_PROVIDER_NATIVE_GUEST_PERMISSIONS_0_SCOPES_0: "*"

steps:
- uses: actions/checkout@v7

- uses: astral-sh/setup-uv@v7
with:
enable-cache: true

- name: Set up Python 3.12
run: uv python install 3.12

- name: Install dependencies
run: uv sync --extra test

- name: Wait for Perses to be ready
run: |
for i in $(seq 1 20); do
curl -sf http://localhost:8080/api/v1/health && break
echo "Waiting for Perses... ($i/20)"
sleep 3
done

- name: Bootstrap Perses admin
run: |
curl -sf -X POST http://localhost:8080/api/v1/users \
-H "Content-Type: application/json" \
-d '{"kind":"User","metadata":{"name":"admin"},"spec":{"firstName":"Admin","lastName":"","nativeProvider":{"password":"password"}}}'

TOKEN=$(curl -sf -X POST http://localhost:8080/api/auth/providers/native/login \
-H "Content-Type: application/json" \
-d '{"login":"admin","password":"password"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

curl -sf -X POST http://localhost:8080/api/v1/globalroles \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"kind":"GlobalRole","metadata":{"name":"admin"},"spec":{"permissions":[{"actions":["*"],"scopes":["*"]}]}}'

curl -sf -X POST http://localhost:8080/api/v1/globalrolebindings \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"kind":"GlobalRoleBinding","metadata":{"name":"admin"},"spec":{"role":"admin","subjects":[{"kind":"User","name":"admin"}]}}'

- name: Run integration tests
env:
PERSES_HOST: http://localhost:8080
PERSES_USERNAME: admin
PERSES_PASSWORD: password
run: uv run pytest tests/integration/ -v
126 changes: 63 additions & 63 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,66 @@ jobs:
- name: Run ruff format check
run: uv tool run ruff format --check .

pr-coverage:
runs-on: ubuntu-latest
permissions: write-all
strategy:
matrix:
python-version: [ '3.10' ]

steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
architecture: x64

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Generate the coverage report
run: export PYTHONPATH=$PWD && uv run --extra dev pytest --junitxml=pytest.xml --cov=perses_api --cov-report xml tests/unit/ | tee pytest-coverage.txt

- name: Execute the coverage checks
uses: MishaKav/pytest-coverage-comment@v1.10.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
pytest-coverage-path: ./pytest-coverage.txt
junitxml-path: ./pytest.xml
hide-badge: true
create-new-comment: true

- name: Generate coverage badge
run: uv run --extra dev genbadge coverage -i coverage.xml -o docs/coverage.svg

- name: Check changed files
uses: tj-actions/verify-changed-files@v20
id: verify-changed-files
with:
files: |
docs

- name: Commit files
if: steps.verify-changed-files.outputs.files_changed == 'true'
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add --force docs
git commit -m "docs: Add coverage badge"

- name: Push changes
uses: ad-m/github-push-action@master
if: steps.verify-changed-files.outputs.files_changed == 'true'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ github.head_ref }}
atomic: false

pr-test:
name: Test / Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
Expand Down Expand Up @@ -57,73 +117,13 @@ jobs:
fail_ci_if_error: false

pr-integration:
name: Integration tests
runs-on: ubuntu-latest
needs: pr-test

services:
perses:
image: persesdev/perses:latest
ports:
- 8080:8080
env:
PERSES_SECURITY_ENABLE_AUTH: "true"
PERSES_SECURITY_AUTHENTICATION_PROVIDERS_ENABLE_NATIVE: "true"
PERSES_SECURITY_AUTHORIZATION_PROVIDER_NATIVE_GUEST_PERMISSIONS_0_ACTIONS_0: "*"
PERSES_SECURITY_AUTHORIZATION_PROVIDER_NATIVE_GUEST_PERMISSIONS_0_SCOPES_0: "*"

steps:
- uses: actions/checkout@v7

- uses: astral-sh/setup-uv@v7
with:
enable-cache: true

- name: Set up Python 3.12
run: uv python install 3.12

- name: Install dependencies
run: uv sync --extra test

- name: Wait for Perses to be ready
run: |
for i in $(seq 1 20); do
curl -sf http://localhost:8080/api/v1/health && break
echo "Waiting for Perses... ($i/20)"
sleep 3
done

- name: Bootstrap Perses admin
run: |
curl -sf -X POST http://localhost:8080/api/v1/users \
-H "Content-Type: application/json" \
-d '{"kind":"User","metadata":{"name":"admin"},"spec":{"firstName":"Admin","lastName":"","nativeProvider":{"password":"password"}}}'

TOKEN=$(curl -sf -X POST http://localhost:8080/api/auth/providers/native/login \
-H "Content-Type: application/json" \
-d '{"login":"admin","password":"password"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

curl -sf -X POST http://localhost:8080/api/v1/globalroles \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"kind":"GlobalRole","metadata":{"name":"admin"},"spec":{"permissions":[{"actions":["*"],"scopes":["*"]}]}}'

curl -sf -X POST http://localhost:8080/api/v1/globalrolebindings \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"kind":"GlobalRoleBinding","metadata":{"name":"admin"},"spec":{"role":"admin","subjects":[{"kind":"User","name":"admin"}]}}'

- name: Run integration tests
env:
PERSES_HOST: http://localhost:8080
PERSES_USERNAME: admin
PERSES_PASSWORD: password
run: uv run pytest tests/integration/ -v
uses: ./.github/workflows/integrationtest.yml
secrets: inherit

pr-docs:
name: Docs
runs-on: ubuntu-latest
needs: [pr-lint, pr-test, pr-integration]
needs: [pr-lint, pr-coverage, pr-test, pr-integration]
steps:
- uses: actions/checkout@v7

Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ jobs:
- name: Set up Python 3.12
run: uv python install 3.12

- name: Create temporary README for package
run: |
sed 's/ *!\[Coverage report\](.*//g' README.md > README.pypi.md
cp README.pypi.md README.md

- name: Build sdist and wheel
run: uv build

Expand Down
9 changes: 7 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ docs:
uv run sphinx-build -W -b html docs docs/_build/html

integration-up:
docker compose up -d --wait
docker compose up -d

integration-down:
docker compose down
Expand All @@ -25,6 +25,11 @@ integration-logs:
docker compose logs perses

integration: integration-up
@for i in $$(seq 1 20); do \
curl -sf http://localhost:8080/api/v1/health && break; \
echo "Waiting for Perses... ($$i/20)"; \
sleep 3; \
done
curl -sf -X POST http://localhost:8080/api/v1/users \
-H "Content-Type: application/json" \
-d '{"kind":"User","metadata":{"name":"admin"},"spec":{"firstName":"Admin","lastName":"","nativeProvider":{"password":"password"}}}' || true
Expand All @@ -42,7 +47,7 @@ integration: integration-up
PERSES_HOST=http://localhost:8080 \
PERSES_USERNAME=admin \
PERSES_PASSWORD=password \
uv run pytest tests/integration/ -v; \
uv run --extra test pytest tests/integration/ -v; \
status=$$?; \
$(MAKE) integration-down; \
exit $$status
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# perses-api-sdk
# perses-api-sdk ![Coverage report](https://github.com/ZPascal/perses_api_sdk/blob/main/docs/coverage.svg)

A Python SDK for the [Perses](https://perses.dev) API.

Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
perses:
image: persesdev/perses:latest
image: persesdev/perses:v0.53.1
ports:
- "8080:8080"
environment:
Expand Down
Empty file removed docs/_static/.gitkeep
Empty file.
1 change: 0 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,3 @@
exclude_patterns = ["_build"]

html_theme = "sphinx_rtd_theme"
html_static_path = ["_static"]
1 change: 1 addition & 0 deletions docs/coverage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,52 @@
Perses API SDK
=============================

A Python SDK for the [Perses](https://perses.dev) API.

Requirements
------------

- Python 3.12+
- A running Perses instance

Installation
------------

Install the package via pip::

pip install perses-api-sdk

Quick Start
-----------

Get started in just a few lines::

from perses_api import APIModel, Project, Dashboard
from perses_api.model import Metadata, ProjectSpec, DashboardSpec
from perses_api.model import Project as ProjectModel, Dashboard as DashboardModel

# Connect with a bearer token
client = APIModel(host="http://localhost:8080", token="<your-token>")

# Or connect with username/password (Basic auth)
client = APIModel(host="http://localhost:8080", username="admin", password="password")

# Create a project
projects = Project(client)
project = projects.create_project(
ProjectModel(metadata=Metadata(name="my-project"), spec=ProjectSpec())
)

# Create a dashboard inside it
dashboards = Dashboard(client)
dashboard = dashboards.create_dashboard(
"my-project",
DashboardModel(
metadata=Metadata(name="my-dashboard", project="my-project"),
spec=DashboardSpec(),
),
)

Perses API SDK
==============

Expand Down
4 changes: 4 additions & 0 deletions perses_api/migrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,8 @@ def migrate(self, grafana_dashboard: dict, migration_input: dict = None) -> dict
if not isinstance(result, dict):
logging.error("Migration failed.")
raise Exception(result)
if "kind" not in result:
error_msg = result.get("message", "Unknown error")
logging.error(f"Migration failed: {error_msg}")
raise Exception(result)
return result
6 changes: 6 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ docs = [
"sphinx>=7.0",
"sphinx-rtd-theme>=2.0",
]
dev = [
"pytest>=7.0",
"pytest-httpx>=0.21",
"pytest-cov>=4.0",
"genbadge[all]>=1.1.0",
]

[tool.setuptools.packages.find]
where = ["."]
Expand Down
Loading
Loading