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
25 changes: 9 additions & 16 deletions .github/workflows/python-CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,17 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
- uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.10"
python-version: "3.11"
- uses: Gr1N/setup-poetry@v8 #install poetry
- name: Install parts of toolchain
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
- name: Upgrade pip
run: python -m pip install --upgrade pip
- name: Install requirements with poetry
run: poetry install
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Lint with Ruff
run: poetry run ruff check --output-format=github .
- name: Test with pytest
run: |
poetry run pytest
run: poetry run pytest
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.10-slim
FROM python:3.11-slim

WORKDIR /data

Expand Down
50 changes: 49 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ Command-line and Python client for downloading and deploying datasets on DBpedia
- [Delete](#cli-delete)
- [Module Usage](#module-usage)
- [Deploy](#module-deploy)
- [Development & Contributing](#development--contributing)
- [Linting](#linting)
- [Testing](#testing)


## Quickstart
Expand All @@ -30,7 +33,7 @@ You can use either **Python** or **Docker**. Both methods support all client fea

### Python

Requirements: [Python](https://www.python.org/downloads/) and [pip](https://pip.pypa.io/en/stable/installation/)
Requirements: [Python 3.11+](https://www.python.org/downloads/) and [pip](https://pip.pypa.io/en/stable/installation/)

Before using the client, install it via pip:

Expand All @@ -43,6 +46,7 @@ You can then use the client in the command line:
```bash
databusclient --help
databusclient deploy --help
databusclient delete --help
databusclient download --help
```

Expand Down Expand Up @@ -183,6 +187,8 @@ Options:
e.g. https://databus.dbpedia.org/sparql)
--vault-token TEXT Path to Vault refresh token file
--databus-key TEXT Databus API key to download from protected databus
--all-versions When downloading artifacts, download all versions
instead of only the latest
--authurl TEXT Keycloak token endpoint URL [default:
https://auth.dbpedia.org/realms/dbpedia/protocol/openid-
connect/token]
Expand Down Expand Up @@ -551,3 +557,45 @@ from databusclient import deploy
# API key can be found (or generated) at https://$$DATABUS_BASE$$/$$USER$$#settings
deploy(dataset, "mysterious API key")
```

## Development & Contributing

Install development dependencies yourself or via [Poetry](https://python-poetry.org/):

```bash
poetry install --with dev
```

### Linting

The used linter is [Ruff](https://ruff.rs/). Ruff is configured in `pyproject.toml` and is enforced in CI (`.github/workflows/ruff.yml`).

For development, you can run linting locally with `ruff check .` and optionally auto-format with `ruff format .`.

To ensure compatibility with the `pyproject.toml` configured dependencies, run Ruff via Poetry:

```bash
# To check for linting issues:
poetry run ruff check .

# To auto-format code:
poetry run ruff format .
```

### Testing

When developing new features please make sure to add appropriate tests and ensure that all tests pass. Tests are under `tests/` and use [pytest](https://docs.pytest.org/en/7.4.x/) as test framework.

When fixing bugs or refactoring existing code, please make sure to add tests that cover the affected functionality. The current test coverage is very low, so any additional tests are highly appreciated.

To run tests locally, use:

```bash
pytest tests/
```

Or to ensure compatibility with the `pyproject.toml` configured dependencies, run pytest via Poetry:

```bash
poetry run pytest tests/
```
3 changes: 2 additions & 1 deletion databusclient/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from databusclient import cli
from databusclient.client import create_dataset, deploy, create_distribution
from databusclient.api.deploy import create_dataset, create_distribution, deploy

__all__ = ["create_dataset", "deploy", "create_distribution"]


def run():
cli.app()
1 change: 1 addition & 0 deletions databusclient/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

60 changes: 42 additions & 18 deletions databusclient/api/delete.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import json
import requests
from typing import List

from databusclient.api.utils import get_databus_id_parts_from_uri, get_json_ld_from_databus
import requests

from databusclient.api.utils import (
fetch_databus_jsonld,
get_databus_id_parts_from_file_url,
)


def _confirm_delete(databusURI: str) -> str:
"""
Expand All @@ -17,9 +22,17 @@ def _confirm_delete(databusURI: str) -> str:
- "cancel" if the user chooses to cancel the entire deletion process
"""
print(f"Are you sure you want to delete: {databusURI}?")
print("\nThis action is irreversible and will permanently remove the resource and all its data.")
print(
"\nThis action is irreversible and will permanently remove the resource and all its data."
)
while True:
choice = input("Type 'yes'/'y' to confirm, 'skip'/'s' to skip this resource, or 'cancel'/'c' to abort: ").strip().lower()
choice = (
input(
"Type 'yes'/'y' to confirm, 'skip'/'s' to skip this resource, or 'cancel'/'c' to abort: "
)
.strip()
.lower()
)
if choice in ("yes", "y"):
return "confirm"
elif choice in ("skip", "s"):
Expand All @@ -30,7 +43,9 @@ def _confirm_delete(databusURI: str) -> str:
print("Invalid input. Please type 'yes'/'y', 'skip'/'s', or 'cancel'/'c'.")


def _delete_resource(databusURI: str, databus_key: str, dry_run: bool = False, force: bool = False):
def _delete_resource(
databusURI: str, databus_key: str, dry_run: bool = False, force: bool = False
):
"""
Delete a single Databus resource (version, artifact, group).

Expand All @@ -56,10 +71,7 @@ def _delete_resource(databusURI: str, databus_key: str, dry_run: bool = False, f
if databus_key is None:
raise ValueError("Databus API key must be provided for deletion")

headers = {
"accept": "*/*",
"X-API-KEY": databus_key
}
headers = {"accept": "*/*", "X-API-KEY": databus_key}

if dry_run:
print(f"[DRY RUN] Would delete: {databusURI}")
Expand All @@ -70,10 +82,14 @@ def _delete_resource(databusURI: str, databus_key: str, dry_run: bool = False, f
if response.status_code in (200, 204):
print(f"Successfully deleted: {databusURI}")
else:
raise Exception(f"Failed to delete {databusURI}: {response.status_code} - {response.text}")
raise Exception(
f"Failed to delete {databusURI}: {response.status_code} - {response.text}"
)


def _delete_list(databusURIs: List[str], databus_key: str, dry_run: bool = False, force: bool = False):
def _delete_list(
databusURIs: List[str], databus_key: str, dry_run: bool = False, force: bool = False
):
"""
Delete a list of Databus resources.

Expand All @@ -85,7 +101,9 @@ def _delete_list(databusURIs: List[str], databus_key: str, dry_run: bool = False
_delete_resource(databusURI, databus_key, dry_run=dry_run, force=force)


def _delete_artifact(databusURI: str, databus_key: str, dry_run: bool = False, force: bool = False):
def _delete_artifact(
databusURI: str, databus_key: str, dry_run: bool = False, force: bool = False
):
"""
Delete an artifact and all its versions.

Expand All @@ -97,7 +115,7 @@ def _delete_artifact(databusURI: str, databus_key: str, dry_run: bool = False, f
- databus_key: Databus API key to authenticate the deletion requests
- dry_run: If True, do not perform the deletion but only print what would be deleted
"""
artifact_body = get_json_ld_from_databus(databusURI, databus_key)
artifact_body = fetch_databus_jsonld(databusURI, databus_key)

json_dict = json.loads(artifact_body)
versions = json_dict.get("databus:hasVersion")
Expand All @@ -121,7 +139,10 @@ def _delete_artifact(databusURI: str, databus_key: str, dry_run: bool = False, f
# Finally, delete the artifact itself
_delete_resource(databusURI, databus_key, dry_run=dry_run, force=force)

def _delete_group(databusURI: str, databus_key: str, dry_run: bool = False, force: bool = False):

def _delete_group(
databusURI: str, databus_key: str, dry_run: bool = False, force: bool = False
):
"""
Delete a group and all its artifacts and versions.

Expand All @@ -133,7 +154,7 @@ def _delete_group(databusURI: str, databus_key: str, dry_run: bool = False, forc
- databus_key: Databus API key to authenticate the deletion requests
- dry_run: If True, do not perform the deletion but only print what would be deleted
"""
group_body = get_json_ld_from_databus(databusURI, databus_key)
group_body = fetch_databus_jsonld(databusURI, databus_key)

json_dict = json.loads(group_body)
artifacts = json_dict.get("databus:hasArtifact", [])
Expand All @@ -143,7 +164,7 @@ def _delete_group(databusURI: str, databus_key: str, dry_run: bool = False, forc
uri = item.get("@id")
if not uri:
continue
_, _, _, _, version, _ = get_databus_id_parts_from_uri(uri)
_, _, _, _, version, _ = get_databus_id_parts_from_file_url(uri)
if version is None:
artifact_uris.append(uri)

Expand All @@ -154,13 +175,14 @@ def _delete_group(databusURI: str, databus_key: str, dry_run: bool = False, forc
# Finally, delete the group itself
_delete_resource(databusURI, databus_key, dry_run=dry_run, force=force)


def delete(databusURIs: List[str], databus_key: str, dry_run: bool, force: bool):
"""
Delete a dataset from the databus.

Delete a group, artifact, or version identified by the given databus URI.
Will recursively delete all data associated with the dataset.

Parameters:
- databusURIs: List of full databus URIs of the resources to delete
- databus_key: Databus API key to authenticate the deletion requests
Expand All @@ -169,7 +191,9 @@ def delete(databusURIs: List[str], databus_key: str, dry_run: bool, force: bool)
"""

for databusURI in databusURIs:
_host, _account, group, artifact, version, file = get_databus_id_parts_from_uri(databusURI)
_host, _account, group, artifact, version, file = (
get_databus_id_parts_from_file_url(databusURI)
)

if group == "collections" and artifact is not None:
print(f"Deleting collection: {databusURI}")
Expand Down
Loading