Skip to content

Drop channels support #336

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 16, 2023
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
24 changes: 0 additions & 24 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,27 +68,3 @@ jobs:
cd tests
python manage.py migrate
python manage.py test

test_channels:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: 3.8
cache: 'pip'
cache-dependency-path: |
tests/requirements-channels.txt

- name: Install Dependencies
run: |
cd tests
python -m pip install --upgrade pip
python -m pip install -r requirements-channels.txt

- name: Run Tests
run: |
cd tests
python manage.py migrate
python manage.py test
3 changes: 1 addition & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,12 @@ COPY ./setup.py ./code/setup.py

# copy example site requires file
COPY ./tests/requirements.txt /code/tests/requirements.txt
COPY ./tests/requirements-channels.txt /code/tests/requirements-channels.txt

WORKDIR /code/tests

RUN pip install --upgrade pip
# Install any needed packages specified in requirements.txt
RUN pip install -r ./requirements-channels.txt
RUN pip install -r ./requirements.txt
RUN pip install gunicorn

# Copy the current directory contents into the container at /code/
Expand Down
26 changes: 4 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ A library to build GraphQL endpoints easily so you can grapple your Wagtail data

## About The Project

![GraphQL Preview Demo](docs/demo.gif)

There is a range of GraphQL packages for Python and specifically Django.
However, getting these packages to work out of the box with an existing infrastructure
without errors isn't as easy to come by.
Expand All @@ -35,28 +33,24 @@ to your model and away you go (although if you want to go deeper you can!).

- Easily create GraphQL types by adding a small annotation in your models.
- Supports traditional Wagtail models:
- Pages (including Streamfield & Orderables)
- Pages (including StreamField & Orderables)
- Snippets
- Images
- Documents
- Media
- Media (via [wagtailmedia](https://pypi.org/project/wagtailmedia/))
- Settings
- Redirects
- Search (on all models)
- Custom Image & Document model support
- Pagination support
- Middleware support
- Advanced headless preview functionality built using GraphQL Subscriptions to enable Page previews on any device!

### Built With

This library is an abstraction upon and relies heavily on Graphene & Graphene Django.
We also use Django Channels and the Potrace image library.

- [Graphene](https://github.com/graphql-python/graphene)
- [Graphene Django](https://github.com/graphql-python/graphene)
- [Potrace](https://github.com/skyrpex/potrace)
- [Django Channels](https://github.com/django/channels) when installed with `wagtail_grapple[channels]`

## Getting Started

Expand All @@ -65,7 +59,7 @@ Getting Grapple installed is designed to be as simple as possible!
### Prerequisites

```
Python>=3.8,<3.12
Python >= 3.8
Wagtail >= 4.1
```

Expand All @@ -88,19 +82,6 @@ INSTALLED_APPS = [
]
```

For GraphQL Subscriptions with Django Channels, run `pip install wagtail_grapple[channels]` and add
`channels` to installed apps:

```python
INSTALLED_APPS = [
# ...
"grapple",
"graphene_django",
"channels",
# ...
]
```

Add the following to the bottom of the same settings file, where each key is the app you want to this library to scan and the value is the prefix you want to give to GraphQL types (you can usually leave this blank):

```python
Expand All @@ -114,6 +95,7 @@ GRAPPLE = {
Add the GraphQL URLs to your `urls.py`:

```python
from django.urls import include, path
from grapple import urls as grapple_urls

# ...
Expand Down
6 changes: 4 additions & 2 deletions docs/general-usage/hooks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,10 @@ Grapple provides a ``register_schema_mutation`` hook that is called when it crea
Subscription
^^^^^^^^^^^^

Note: subscriptions are only enabled when Grapple is installed with Django Channels: ``pip install wagtail_grapple[channels]``.
Grapple provides a ``register_schema_subscription`` hook that is called when it creates the schema. You can use it to add your custom ``Subscription`` mixins
Note: previously subscriptions were only enabled when Grapple was installed with Django Channels. We no longer provide
out of the box support for subscriptions due to incompatibilities in the various dependencies.
The ``register_schema_subscription`` hook is still provided. It is called when Grapple creates the schema.
You can use it to add your custom ``Subscription`` mixins and functionality.

.. code-block:: python
import asyncio
Expand Down
113 changes: 2 additions & 111 deletions docs/general-usage/preview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,114 +6,5 @@ Grapple also provides support for headless previews using the `Wagtail Headless
This means you can pass a unique 'preview token' to the GraphQL endpoint and preview a page as you update
it in the admin (in real-time!).

Grapple's Headless Preview is built-on GraphQL Subscriptions which means
that your client subscribes to the preview page and any changes in the Admin
will be pushed to your client via WebSockets. This allows you to add preview
support to any client whether that be a SPA or Native App.

Setup
^^^^^

See :ref:`usage with subscriptions<usage-with-subscriptions>` first to make sure you installed Django Channels when you installed Grapple.
Your installed apps in your settings should look like so:

.. code-block:: python

INSTALLED_APPS = [
# ...
"grapple",
"graphene_django",
"channels",
"wagtail_headless_preview",
# ...
]

Now you need to run the migrations that come with Wagtail Headless Preview.

::

$ python manage.py migrate



You also want to add to your settings the URL you want to redirect to when the
user clicks the 'Preview' button:

.. code-block:: python

HEADLESS_PREVIEW_CLIENT_URLS = {
"default": "http://localhost:8001/preview",
}

HEADLESS_PREVIEW_LIVE = True

Two HTTP params are also passed to this url:
- ``content_type``: The content type string of the Model you're viewing.
- ``token``: The preview token you need to retrieve the preview data.

*Attention*: When the user clicks the "Preview" or "View Draft" button in the Wagtail admin the preview opens in a new tab. The URL in the preview tab will not reveal the actual preview URL set in the setting. It will rather show the admin URL of the page with an additional URL element ``/preview`` or ``/view_draft`` (e.g. ``http://localhost:8000/admin/pages/5/edit/preview/``).


Your next step is to subclass any Page models you want headless preview on with
``wagtail_headless_preview.models.HeadlessPreviewMixin`` to override the original Wagtail preview methods:

.. code-block:: python

from wagtail_headless_preview.models import HeadlessPreviewMixin


class BlogPage(HeadlessPreviewMixin, Page):
author = models.CharField(max_length=255)
date = models.DateField("Post date")
advert = models.ForeignKey(
"home.Advert",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="+",
)


And you are done!


How to use
^^^^^^^^^^

Now when you click the 'Preview' button in Wagtail a page will open with the content of
your defined `HEADLESS_PREVIEW_CLIENT_URLS`. You can either load the preview token through
the HTTP params or through the ``used-token`` cookie which has been set in
both the Admin and the redirected page.

Once you've pushed the 'Preview' button, Any data input into the Admin form
will be pushed to your subscribed client. Pushing the button again will start
a new session with a new token.

To access the preview data you can use one of the three queries (the 'subscription'
declaration is optional if you want to do a one time query):

::

subscription {
page(contentType: "home.blogpage", token: "id=4:1hg5FZ:bPmOihaRCLGbo4mzZagvrJAqNWM") {
title
}
}


::

subscription {
page(slug: "example-blog-page", token: "id=4:1hg5FZ:bPmOihaRCLGbo4mzZagvrJAqNWM") {
title
}
}


::

subscription {
page(id: 1, token: "id=4:1hg5FZ:bPmOihaRCLGbo4mzZagvrJAqNWM") {
title
}
}
See the `Wagtail Headless Preview<https://github.com/torchbox/wagtail-headless-preview>`_ documentation for setup
instructions.
23 changes: 0 additions & 23 deletions docs/getting-started/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,26 +58,3 @@ By default, Grapple uses :doc:`these settings <../general-usage/settings>`.


*Your GraphQL endpoint is available at http://localhost:8000/graphql/*

.. _usage-with-subscriptions:
Usage with subscriptions
========================

To enable GraphQL Subscriptions, you need to install Grapple with Django Channels.
Run ``pip install wagtail_grapple[channels]`` and add ``channels`` to installed apps:

.. code-block:: python

INSTALLED_APPS = [
# ...
"grapple",
"graphene_django",
"channels",
# ...
]

Add the following Django Channels configuration to your settings.

.. code-block:: python

ASGI_APPLICATION = "graphql_ws.django.routing.application"
32 changes: 12 additions & 20 deletions grapple/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
# to be a nice way to disable this validator so we monkey-patch it instead.


# We can't simply override specified_rules because it's a tuple and immutable. Instead we are
# monkey patching the NoUnusedFragmentRule.leave_document so it doesn't do any validation.
# We can't simply override specified_rules because it's a tuple and immutable. Instead, we are
# monkey patching the NoUnusedFragmentRule.leave_document, so it doesn't do any validation.
NoUnusedFragmentsRule.leave_document = lambda self, *_args: None


Expand All @@ -23,8 +23,6 @@ def create_schema():
It inherits its queries from each of the specific type mixins.
"""

from .settings import has_channels

query_mixins = []
for fn in hooks.get_hooks("register_schema_query"):
fn(query_mixins)
Expand All @@ -40,34 +38,28 @@ class Query(*query_mixins):
for fn in hooks.get_hooks("register_schema_mutation"):
fn(mutation_mixins)

# ensure graphene.ObjectType is always present
if graphene.ObjectType not in mutation_mixins:
mutation_mixins.append(graphene.ObjectType)

if len(mutation_mixins) > 1:
if len(mutation_mixins) > 0:
# ensure graphene.ObjectType is always present
if graphene.ObjectType not in mutation_mixins:
mutation_mixins.append(graphene.ObjectType)

class Mutation(*mutation_mixins):
pass

else:
Mutation = None

if has_channels:
subscription_mixins = []
for fn in hooks.get_hooks("register_schema_subscription"):
fn(subscription_mixins)
subscription_mixins = []
for fn in hooks.get_hooks("register_schema_subscription"):
fn(subscription_mixins)

if len(subscription_mixins) > 0:
# ensure graphene.ObjectType is always present
if graphene.ObjectType not in subscription_mixins:
subscription_mixins.append(graphene.ObjectType)

if len(subscription_mixins) > 1:

class Subscription(*subscription_mixins):
pass

else:
Subscription = None
class Subscription(*subscription_mixins):
pass

else:
Subscription = None
Expand Down
7 changes: 0 additions & 7 deletions grapple/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@

logger = logging.getLogger("grapple")

try:
import channels # noqa: F401

has_channels = True
except ImportError:
has_channels = False


DEFAULTS = {
"APPS": [],
Expand Down
Loading