diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 585584e..8be7b69 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -32,7 +32,7 @@ jobs: python -m pip install -e '.[dev]' - name: Lint with flake8 run: | - flake8 . --count --max-complexity=10 --max-line-length=127 --statistics + flake8 . --count --max-line-length=127 --statistics - name: Test with pytest run: | pytest diff --git a/README.md b/README.md index 6aa5b33..fd682b4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Python package](https://github.com/dnlbauer/django-signposting/actions/workflows/python-package.yml/badge.svg)](https://github.com/dnlbauer/django-signposting/actions/workflows/python-package.yml) -# FAIR signposting for Django +# FAIR signposting middleware for Django `django_signposting` is a Django middleware library that facilitates the addition of FAIR signposting headers to HTTP responses. @@ -11,6 +11,7 @@ Based on the [Signposting](https://github.com/stain/signposting) library. ## Features - Automatically adds signposting headers to HTTP responses. +- Signposts can be added manually or automatically be parsed from JSON-LD/schema.org - Supports multiple relation types with optional media type specification. - Easily integrable with existing Django applications. @@ -22,9 +23,30 @@ pip install django_signposting ## Usage -### 1. Add Middleware +### Automatic parsing of JSON-LD -Add the middleware to your Django project's `MIDDLEWARE` setting in `settings.py`: +To enable automatic parsing of JSON-LD, add the following middleware classes to your Django project's MIDDLEWARE setting in settings.py: + +```python +MIDDLEWARE = [ + ..., + 'django_signposting.middleware.SignpostingMiddleware', + 'django_signposting.middleware.JsonLdSignpostingParserMiddleware', + ..., +] +``` + +This setup allows django_signposting to extract JSON-LD embedded in HTML ` + + + + """) + response.status_code = 200 + + middleware = JsonLdSignpostingParserMiddleware(lambda request: response) + response = middleware(HttpRequest()) + + for expected_signpost in expected_signposts: + assert expected_signpost in response._signposts + + assert len(expected_signposts) == len(response._signposts) + + +def test_jsonld_signposting_basic(): + jsonld_test_runner( + { + "@context": "http://schema.org", + "@type": "WebPage", + "author": {"url": "http://example.com/author"}, + "url": "http://example.com/url", + "license": {"identifier": "http://example.com/license"}, + "hasPart": [ + { + "url": "http://example.com/image1.jpg", + }, + { + "url": "http://example.com/image2.jpg", + }, + ], + "sameAs": [ + { + "url": "http://example.com/metadata.json", + }, + { + "url": "http://example.com/metadata.xml", + }, + ], + }, + [ + Signpost(LinkRel.type, "http://schema.org/WebPage"), + Signpost(LinkRel.author, "http://example.com/author"), + Signpost(LinkRel.cite_as, "http://example.com/url"), + Signpost(LinkRel.license, "http://example.com/license"), + Signpost(LinkRel.item, "http://example.com/image1.jpg"), + Signpost(LinkRel.item, "http://example.com/image2.jpg"), + Signpost(LinkRel.describedby, "http://example.com/metadata.json"), + Signpost(LinkRel.describedby, "http://example.com/metadata.xml"), + ], + ) + + +def test_jsonld_empty_signposting(): + jsonld_test_runner({}, []) + + +def test_jsonld_signposting_media_types(): + jsonld_test_runner( + { + "@context": "http://schema.org", + "@type": "WebPage", + "author": {"url": "http://example.com/author"}, + "url": "http://example.com/url", + "license": {"identifier": "http://example.com/license"}, + "hasPart": [ + { + "url": "http://example.com/image1.jpg", + "encodingFormat": "image/jpeg", + }, + {"url": "http://example.com/image2.jpg", "encodingFormat": "image/png"}, + ], + "sameAs": [ + { + "url": "http://example.com/metadata.json", + "encodingFormat": "application/json", + }, + { + "url": "http://example.com/metadata.xml", + "encodingFormat": "application/xml", + }, + ], + }, + [ + Signpost(LinkRel.type, "http://schema.org/WebPage"), + Signpost(LinkRel.author, "http://example.com/author"), + Signpost(LinkRel.cite_as, "http://example.com/url"), + Signpost(LinkRel.license, "http://example.com/license"), + Signpost(LinkRel.item, "http://example.com/image1.jpg", "image/jpeg"), + Signpost(LinkRel.item, "http://example.com/image2.jpg", "image/png"), + Signpost( + LinkRel.describedby, + "http://example.com/metadata.json", + "application/json", + ), + Signpost( + LinkRel.describedby, + "http://example.com/metadata.xml", + "application/xml", + ), + ], + ) + + +def test_jsonld_signposting_property_precedence(): + jsonld_test_runner( + { + "@context": "http://schema.org", + "@type": "WebPage", + "author": { + "identifier": "http://example.com/author-ident", + "url": "http://example.com/author-url", + }, + "hasPart": [ + { + "identifier": "http://example.com/image1-identifier.jpg", + "url": "http://example.com/image1-url.jpg", + "contentUrl": "http://example.com/image1-content.jpg", + }, + { + "identifier": "http://example.com/image2-identifier.jpg", + "url": "http://example.com/image2-url.jpg", + }, + ], + }, + [ + Signpost(LinkRel.type, "http://schema.org/WebPage"), + Signpost(LinkRel.author, "http://example.com/author-url"), + Signpost(LinkRel.item, "http://example.com/image1-content.jpg"), + Signpost(LinkRel.item, "http://example.com/image2-url.jpg"), + ], + ) + + +def test_jsonld_signposting_ids(): + jsonld_test_runner( + { + "@context": "http://schema.org", + "@graph": [ + { + "@type": "WebPage", + "author": {"@id": "http://example.com/author"}, + "url": "http://example.com/url", + "license": {"@id": "http://example.com/license"}, + "hasPart": [ + { + "@id": "http://example.com/image1.jpg", + }, + ], + "sameAs": [ + { + "@id": "http://example.com/metadata.json", + }, + ], + }, + {"@id": "http://example.com/author", "@type": "Person"}, + {"@id": "http://example.com/license"}, + { + "@id": "http://example.com/image1.jpg", + "encodingFormat": "image/jpeg", + }, + {"@id": "http://example.com/metadata.json"}, + ], + }, + [ + Signpost(LinkRel.type, "http://schema.org/WebPage"), + Signpost(LinkRel.author, "http://example.com/author"), + Signpost(LinkRel.cite_as, "http://example.com/url"), + Signpost(LinkRel.license, "http://example.com/license"), + Signpost(LinkRel.item, "http://example.com/image1.jpg", "image/jpeg"), + Signpost(LinkRel.describedby, "http://example.com/metadata.json"), + ], + ) + + +def test_jsonld_signposting_ids_with_url(): + jsonld_test_runner( + { + "@context": "http://schema.org", + "@graph": [ + { + "@type": "WebPage", + "author": {"@id": "http://example.com/author"}, + }, + { + "@id": "http://example.com/author", + "url": "http://example.com/realAuthorUrl", + }, + ], + }, + [ + Signpost(LinkRel.type, "http://schema.org/WebPage"), + Signpost(LinkRel.author, "http://example.com/realAuthorUrl"), + ], + ) + + +def test_jsonld_signposting_multiple_types(): + jsonld_test_runner( + { + "@context": "http://schema.org", + "@type": "Dataset", + "author": "http://example.com/author", + }, + [ + Signpost(LinkRel.type, "http://schema.org/Dataset"), + Signpost(LinkRel.author, "http://example.com/author"), + ], + ) diff --git a/tests/test_middleware.py b/tests/test_signposting_middleware.py similarity index 100% rename from tests/test_middleware.py rename to tests/test_signposting_middleware.py