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
32 changes: 32 additions & 0 deletions .github/workflows/dots.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Validate DoTS demo API

on: [push]

jobs:
build:

runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10"]

steps:
- uses: actions/checkout@v4
- name: Setup Python # Set Python version
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
# Install pip and pytest
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .
- name: Test with pytest
run: pytest -s --entry-endpoint=https://dots.chartes.psl.eu/demo/api/dts/ --html=reports/dots/${{matrix.python-version }}_report.html
- name: Upload pytest test results
uses: actions/upload-artifact@v4
with:
name: pytest-results-${{ matrix.python-version }}
path: reports/dots/
# Use always() to always run this step to publish test results when there are test failures
if: ${{ always() }}
2 changes: 1 addition & 1 deletion .github/workflows/dracor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
python -m pip install --upgrade pip
pip install .
- name: Test with pytest
run: pytest -s --entry-endpoint=https://dev.dracor.org/api/v1/dts --html=reports/dracor/${{matrix.python-version }}_report.html
run: pytest -s --entry-endpoint=https://staging.dracor.org/api/v1/dts --html=reports/dracor/${{matrix.python-version }}_report.html
- name: Upload pytest test results
uses: actions/upload-artifact@v4
with:
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
__pycache__
.DS_Store
.DS_Store
reports/
70 changes: 48 additions & 22 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
SHELL:=/bin/bash

UBHD_DTS_API?=https://digi.ub.uni-heidelberg.de/editionService/dts/
DRACOR_DTS_API?=https://dev.dracor.org/api/v1/dts
DRACOR_DTS_API?=https://staging.dracor.org/api/v1/dts
DoTS_DTS_API?=https://dots.chartes.psl.eu/demo/api/dts/
FTSR_DTS_API?=http://ftsr-dev.unil.ch:9090/api/dts/

REPORTS_DIR?=reports
MOCK_REPORTS_DIR=$(REPORTS_DIR)/docs/
UBHD_REPORTS_DIR=$(REPORTS_DIR)/ubhd/
DRACOR_REPORTS_DIR=$(REPORTS_DIR)/dracor/
FTSR_REPORTS_DIR=$(REPORTS_DIR)/ftsr/
DoTS_REPORTS_DIR=$(REPORTS_DIR)/dots/

##################################################
# Tests on mock data + examples from docs #
Expand All @@ -26,33 +28,16 @@ test-collection:
test-navigation:
pytest tests/test_navigation_endpoint.py -s --html=$(MOCK_REPORTS_DIR)/report-navigation.html


#####################
# UNIL FTSR API #
#####################

test-ftsr-all:
pytest --entry-endpoint=$(FTSR_DTS_API) -s --html=$(FTSR_REPORTS_DIR)/ftsr_report.html

test-ftsr-entry:
pytest tests/test_entry_endpoint.py --entry-endpoint=$(FTSR_DTS_API) -s --html=$(FTSR_REPORTS_DIR)/ftsr_entry_report.html

test-ftsr-collection:
pytest tests/test_collection_endpoint.py --entry-endpoint=$(FTSR_DTS_API) -s --html=$(FTSR_REPORTS_DIR)/ftsr_collection_report.html

test-ftsr-navigation:
pytest tests/test_navigation_endpoint.py --entry-endpoint=$(FTSR_DTS_API) -s --html=$(FTSR_REPORTS_DIR)/ftsr_entry_report.html

test-ftsr-document:
pytest tests/test_document_endpoint.py --entry-endpoint=$(FTSR_DTS_API) -s --html=$(FTSR_REPORTS_DIR)/ftsr_document_report.html

#####################
# DraCor API #
#####################

test-dracor-all:
pytest --entry-endpoint=$(DRACOR_DTS_API) -s --html=$(DRACOR_REPORTS_DIR)/dracor_all_report.html

debug-dracor-all:
pytest --pdb --entry-endpoint=$(DRACOR_DTS_API) -s --html=$(DRACOR_REPORTS_DIR)/dracor_all_report.html

test-dracor-entry:
pytest tests/test_entry_endpoint.py --entry-endpoint=$(DRACOR_DTS_API) -s --html=$(DRACOR_REPORTS_DIR)/dracor_entry_report.html

Expand Down Expand Up @@ -88,4 +73,45 @@ test-ubhd-document:
pytest tests/test_document_endpoint.py --entry-endpoint=$(UBHD_DTS_API) -s --html=$(UBHD_REPORTS_DIR)/ubhd_document_report.html

test-ubhd-strict:
pytest --entry-endpoint=$(UBHD_DTS_API) -W error::DeprecationWarning -s --html=$(UBHD_REPORTS_DIR)/ubhd_report.html
pytest --entry-endpoint=$(UBHD_DTS_API) -W error::DeprecationWarning -s --html=$(UBHD_REPORTS_DIR)/ubhd_report.html

#####################
# DoTS API #
#####################

test-dots-all:
pytest --entry-endpoint=$(DoTS_DTS_API) -s --html=$(DoTS_REPORTS_DIR)/dots_all_report.html

test-dots-entry:
pytest tests/test_entry_endpoint.py --entry-endpoint=$(DoTS_DTS_API) -s --html=$(DoTS_REPORTS_DIR)/dots_entry_report.html

test-dots-collection:
pytest tests/test_collection_endpoint.py --entry-endpoint=$(DoTS_DTS_API) -s --html=$(DoTS_REPORTS_DIR)/dots_collection_report.html

test-dots-navigation:
pytest tests/test_navigation_endpoint.py --entry-endpoint=$(DoTS_DTS_API) -s --html=$(DoTS_REPORTS_DIR)/dots_navigation_report.html

test-dots-document:
pytest tests/test_document_endpoint.py --entry-endpoint=$(DoTS_DTS_API) -s --html=$(DoTS_REPORTS_DIR)/dots_document_report.html

test-dots-strict:
pytest --entry-endpoint=$(DoTS_DTS_API) -W error::DeprecationWarning -s --html=$(DoTS_REPORTS_DIR)/dots_report.html

#####################
# UNIL FTSR API #
#####################

test-ftsr-all:
pytest --entry-endpoint=$(FTSR_DTS_API) -s --html=$(FTSR_REPORTS_DIR)/ftsr_report.html

test-ftsr-entry:
pytest tests/test_entry_endpoint.py --entry-endpoint=$(FTSR_DTS_API) -s --html=$(FTSR_REPORTS_DIR)/ftsr_entry_report.html

test-ftsr-collection:
pytest tests/test_collection_endpoint.py --entry-endpoint=$(FTSR_DTS_API) -s --html=$(FTSR_REPORTS_DIR)/ftsr_collection_report.html

test-ftsr-navigation:
pytest tests/test_navigation_endpoint.py --entry-endpoint=$(FTSR_DTS_API) -s --html=$(FTSR_REPORTS_DIR)/ftsr_entry_report.html

test-ftsr-document:
pytest tests/test_document_endpoint.py --entry-endpoint=$(FTSR_DTS_API) -s --html=$(FTSR_REPORTS_DIR)/ftsr_document_report.html
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,17 @@ For more examples, see the commands contained in the [`Makefile`](./Makefile).

</details>

## Validation of known implementations
### How to use the validator as a DTS client

<details>
<summary>Show more</summary>
See the examples in notebook [`example_dts_client.ipynb`](./example_dts_client.ipynb).
</details>

## Validation of known current implementations

| Name | API entry endpoint | DTS version |Validation status |
|-------|-----|-------------|-------------------|
| DraCor | https://dev.dracor.org/api/v1/dts | `unstable`|[![Validate DraCor (dev) API](https://github.com/mromanello/DTS-validator/actions/workflows/dracor.yml/badge.svg)](https://github.com/mromanello/DTS-validator/actions/workflows/dracor.yml) |
| DraCor | https://staging.dracor.org/api/v1/dts | `unstable`|[![Validate DraCor (dev) API](https://github.com/mromanello/DTS-validator/actions/workflows/dracor.yml/badge.svg)](https://github.com/mromanello/DTS-validator/actions/workflows/dracor.yml) |
| UBHD | https://digi.ub.uni-heidelberg.de/editionService/dts/ | `unstable`|[![Validate UBHD API](https://github.com/mromanello/DTS-validator/actions/workflows/ubhd.yml/badge.svg)](https://github.com/mromanello/DTS-validator/actions/workflows/ubhd.yml) |
|DoTS demo |https://dots.chartes.psl.eu/demo/api/dts/|`1-alpha`|[![Validate DoTS API](https://github.com/mromanello/DTS-validator/actions/workflows/dots.yml/badge.svg)](https://github.com/mromanello/DTS-validator/actions/workflows/dots.yml)|
43 changes: 34 additions & 9 deletions dts_validator/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def __repr__(self) -> str:

class DTS_CitableUnit(object):
"""Class representing a DTS CitableUnit object.
As per the DTS documentation, a `CitableUnit` is a portion of a `Resource` identified by a reference string."""
A `CitableUnit` is a portion of a `Resource` identified by a reference string."""
def __init__(self, raw_json) -> None:
self._json = raw_json
self.id = raw_json["identifier"]
Expand All @@ -76,6 +76,7 @@ def __init__(self, raw_json) -> None:

# populate additional properties from a DTS Navigation endpoint response JSON
if 'member' in self._json and self._json['member']:
# TODO: make sure that `member` contains a list and not a dictionary !
for unit in self._json['member']:
self.citable_units.append(DTS_CitableUnit(unit))

Expand All @@ -93,24 +94,47 @@ def __repr__(self) -> str:

# TODO: find a cleaner way of triggering the header validation
class DTS_API(object):
def __init__(self, entry_endpoint_uri) -> None:
def __init__(self, entry_endpoint_uri, enable_validation: bool = True) -> None:
self._enable_validation = enable_validation
req = requests.get(entry_endpoint_uri)
assert 'application/ld+json' in req.headers['Content-Type'] # TODO: wrap around a try/except statement

if self.is_validation_enabled:
try:
assert 'application/ld+json' in req.headers['Content-Type'] # TODO: wrap around a try/except statement
except AssertionError:
print('Missing `application/ld+json` in Content-Type header')

self._entry_endpoint_json = req.json()


# before using the URI templates, let's make sure that they are
# declared by the Entry endpoint as expected
check_required_property(self._entry_endpoint_json, 'collection')
check_required_property(self._entry_endpoint_json, 'document')
check_required_property(self._entry_endpoint_json, 'navigation')
if self.is_validation_enabled:
check_required_property(self._entry_endpoint_json, 'collection')
check_required_property(self._entry_endpoint_json, 'document')
check_required_property(self._entry_endpoint_json, 'navigation')
else:
LOGGER.info('Skipping validation of required properties in the Entry endpoint JSON')

# initialise URI templates
self._collection_endpoint_template = URITemplate(self._entry_endpoint_json['collection'])
self._document_endpoint_template = URITemplate(self._entry_endpoint_json['document'])
self._navigation_endpoint_template = URITemplate(self._entry_endpoint_json['navigation'])
try:
self._collection_endpoint_template = URITemplate(self._entry_endpoint_json['collection'])
self._document_endpoint_template = URITemplate(self._entry_endpoint_json['document'])
self._navigation_endpoint_template = URITemplate(self._entry_endpoint_json['navigation'])
except KeyError as e:
msg = f"""
Missing required URI template in the Entry endpoint JSON: {e}.
Got keys: {self._entry_endpoint_json.keys()}
"""
LOGGER.error(msg)
raise Exception(msg)

# TODO pagination can be supported in collection or navigation endpoints => check that

@property
def is_validation_enabled(self) -> bool:
return self._enable_validation

def collections(
self, id: Optional[str] = None,
recursive: bool = False,
Expand Down Expand Up @@ -205,6 +229,7 @@ def navigation(
else:
return (None, response)

# TODO: add support for `mediaType` parameter
def document(
self,
resource: DTS_Resource,
Expand Down
Loading
Loading