Skip to content

Commit

Permalink
Add page webhooks (#6787)
Browse files Browse the repository at this point in the history
  • Loading branch information
d-wysocki committed Jan 27, 2021
1 parent 805dde7 commit fbfc66e
Show file tree
Hide file tree
Showing 11 changed files with 304 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ All notable, unreleased changes to this project will be documented in this file.
- Introduce page reference attributes - #6624 by @IKarbowiak
- Introduce product reference attributes - #6711 by @IKarbowiak
- Add metadata to warehouse - #6727 by @d-wysocki
- Add page webhooks: `PAGE_CREATED`, `PAGE_UPDATED` and `PAGE_DELETED` - #6787 by @d-wysocki

# 2.11.1

Expand Down
17 changes: 17 additions & 0 deletions saleor/graphql/page/mutations/pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ def _save_m2m(cls, info, instance, cleaned_data):
if attributes:
AttributeAssignmentMixin.save(instance, attributes)

@classmethod
def save(cls, info, instance, cleaned_input):
super().save(info, instance, cleaned_input)
info.context.plugins.page_created(instance)


class PageUpdate(PageCreate):
class Arguments:
Expand All @@ -123,6 +128,11 @@ class Meta:
error_type_class = PageError
error_type_field = "page_errors"

@classmethod
def save(cls, info, instance, cleaned_input):
super(PageCreate, cls).save(info, instance, cleaned_input)
info.context.plugins.page_updated(instance)


class PageDelete(ModelDeleteMutation):
class Arguments:
Expand All @@ -135,6 +145,13 @@ class Meta:
error_type_class = PageError
error_type_field = "page_errors"

@classmethod
def perform_mutation(cls, _root, info, **data):
page = cls.get_instance(info, **data)
response = super().perform_mutation(_root, info, **data)
info.context.plugins.page_deleted(page)
return response


class PageTypeCreateInput(graphene.InputObjectType):
name = graphene.String(description="Name of the page type.")
Expand Down
147 changes: 131 additions & 16 deletions saleor/graphql/page/tests/test_page.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import datetime
from unittest import mock

import graphene
import pytest
from django.utils import timezone
Expand All @@ -9,6 +12,8 @@
from ....page.error_codes import PageErrorCode
from ....page.models import Page, PageType
from ....tests.utils import dummy_editorjs
from ....webhook.event_types import WebhookEventType
from ....webhook.payloads import generate_page_payload
from ...tests.utils import get_graphql_content

PAGE_QUERY = """
Expand Down Expand Up @@ -264,6 +269,49 @@ def test_page_create_mutation(staff_api_client, permission_manage_pages, page_ty
assert tag_value_slug in values


@mock.patch("saleor.plugins.webhook.plugin.trigger_webhooks_for_event.delay")
def test_page_create_trigger_page_webhook(
mocked_webhook_trigger,
staff_api_client,
permission_manage_pages,
page_type,
settings,
):
settings.PLUGINS = ["saleor.plugins.webhook.plugin.WebhookPlugin"]

page_slug = "test-slug"
page_content = dummy_editorjs("test content", True)
page_title = "test title"
page_is_published = True
page_type_id = graphene.Node.to_global_id("PageType", page_type.pk)
# test creating root page
variables = {
"title": page_title,
"content": page_content,
"isPublished": page_is_published,
"slug": page_slug,
"pageType": page_type_id,
}

response = staff_api_client.post_graphql(
CREATE_PAGE_MUTATION, variables, permissions=[permission_manage_pages]
)
content = get_graphql_content(response)
data = content["data"]["pageCreate"]
assert data["pageErrors"] == []
assert data["page"]["title"] == page_title
assert data["page"]["content"] == page_content
assert data["page"]["slug"] == page_slug
assert data["page"]["isPublished"] == page_is_published
assert data["page"]["pageType"]["id"] == page_type_id
page = Page.objects.first()
expected_data = generate_page_payload(page)

mocked_webhook_trigger.assert_called_once_with(
WebhookEventType.PAGE_CREATED, expected_data
)


def test_page_create_required_fields(
staff_api_client, permission_manage_pages, page_type
):
Expand Down Expand Up @@ -956,25 +1004,27 @@ def test_create_page_with_product_reference_attribute_required_no_references_giv
assert errors[0]["attributes"] == [file_attribute_id]


def test_page_delete_mutation(staff_api_client, page, permission_manage_pages):
query = """
mutation DeletePage($id: ID!) {
pageDelete(id: $id) {
page {
title
id
}
pageErrors {
field
code
message
}
}
PAGE_DELETE_MUTATION = """
mutation DeletePage($id: ID!) {
pageDelete(id: $id) {
page {
title
id
}
"""
pageErrors {
field
code
message
}
}
}
"""


def test_page_delete_mutation(staff_api_client, page, permission_manage_pages):
variables = {"id": graphene.Node.to_global_id("Page", page.id)}
response = staff_api_client.post_graphql(
query, variables, permissions=[permission_manage_pages]
PAGE_DELETE_MUTATION, variables, permissions=[permission_manage_pages]
)
content = get_graphql_content(response)
data = content["data"]["pageDelete"]
Expand All @@ -983,6 +1033,28 @@ def test_page_delete_mutation(staff_api_client, page, permission_manage_pages):
page.refresh_from_db()


@mock.patch("saleor.plugins.webhook.plugin.trigger_webhooks_for_event.delay")
def test_page_delete_trigger_webhook(
mocked_webhook_trigger, staff_api_client, page, permission_manage_pages, settings
):
settings.PLUGINS = ["saleor.plugins.webhook.plugin.WebhookPlugin"]
variables = {"id": graphene.Node.to_global_id("Page", page.id)}
response = staff_api_client.post_graphql(
PAGE_DELETE_MUTATION, variables, permissions=[permission_manage_pages]
)
content = get_graphql_content(response)
data = content["data"]["pageDelete"]
assert data["page"]["title"] == page.title
with pytest.raises(page._meta.model.DoesNotExist):
page.refresh_from_db()

expected_data = generate_page_payload(page)

mocked_webhook_trigger.assert_called_once_with(
WebhookEventType.PAGE_DELETED, expected_data
)


UPDATE_PAGE_MUTATION = """
mutation updatePage(
$id: ID!, $input: PageInput!
Expand Down Expand Up @@ -1089,6 +1161,49 @@ def test_update_page(staff_api_client, permission_manage_pages, page):
assert attr_data in expected_attributes


@freeze_time("2020-03-18 12:00:00")
@mock.patch("saleor.plugins.webhook.plugin.trigger_webhooks_for_event.delay")
def test_update_page_trigger_webhook(
mocked_webhook_trigger, staff_api_client, permission_manage_pages, page, settings
):
query = UPDATE_PAGE_MUTATION

settings.PLUGINS = ["saleor.plugins.webhook.plugin.WebhookPlugin"]

page_title = page.title
new_slug = "new-slug"
assert new_slug != page.slug

page_id = graphene.Node.to_global_id("Page", page.id)

variables = {
"id": page_id,
"input": {
"slug": new_slug,
"isPublished": True,
},
}

# when
response = staff_api_client.post_graphql(
query, variables, permissions=[permission_manage_pages]
)

# then
content = get_graphql_content(response)
data = content["data"]["pageUpdate"]

assert not data["pageErrors"]
assert data["page"]["title"] == page_title
assert data["page"]["slug"] == new_slug
page.publication_date = datetime.date(2020, 3, 18)
expected_data = generate_page_payload(page)

mocked_webhook_trigger.assert_called_once_with(
WebhookEventType.PAGE_UPDATED, expected_data
)


def test_update_page_with_file_attribute_value(
staff_api_client, permission_manage_pages, page, page_file_attribute
):
Expand Down
6 changes: 6 additions & 0 deletions saleor/graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -5790,6 +5790,9 @@ enum WebhookEventTypeEnum {
CHECKOUT_CREATED
CHECKOUT_UPDATED
FULFILLMENT_CREATED
PAGE_CREATED
PAGE_UPDATED
PAGE_DELETED
}

enum WebhookSampleEventTypeEnum {
Expand All @@ -5809,6 +5812,9 @@ enum WebhookSampleEventTypeEnum {
CHECKOUT_CREATED
CHECKOUT_UPDATED
FULFILLMENT_CREATED
PAGE_CREATED
PAGE_UPDATED
PAGE_DELETED
}

type WebhookUpdate {
Expand Down
8 changes: 8 additions & 0 deletions saleor/graphql/webhook/tests/test_webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,9 @@ def test_query_webhook_events_without_permissions(staff_api_client):
(WebhookSampleEventTypeEnum.CHECKOUT_CREATED, False),
(WebhookSampleEventTypeEnum.CHECKOUT_UPDATED, False),
(WebhookSampleEventTypeEnum.FULFILLMENT_CREATED, True),
(WebhookSampleEventTypeEnum.PAGE_CREATED, False),
(WebhookSampleEventTypeEnum.PAGE_UPDATED, False),
(WebhookSampleEventTypeEnum.PAGE_DELETED, False),
],
)
def test_sample_payload_query_by_app(
Expand Down Expand Up @@ -466,6 +469,9 @@ def test_sample_payload_query_by_app(
(WebhookSampleEventTypeEnum.CHECKOUT_CREATED, True),
(WebhookSampleEventTypeEnum.CHECKOUT_UPDATED, True),
(WebhookSampleEventTypeEnum.FULFILLMENT_CREATED, False),
(WebhookSampleEventTypeEnum.PAGE_CREATED, True),
(WebhookSampleEventTypeEnum.PAGE_UPDATED, True),
(WebhookSampleEventTypeEnum.PAGE_DELETED, True),
],
)
def test_sample_payload_query_by_staff(
Expand All @@ -476,12 +482,14 @@ def test_sample_payload_query_by_staff(
permission_manage_users,
permission_manage_products,
permission_manage_checkouts,
permission_manage_pages,
):
mock_generate_sample_payload.return_value = {"mocked_response": ""}
query = SAMPLE_PAYLOAD_QUERY
staff_api_client.user.user_permissions.add(permission_manage_users)
staff_api_client.user.user_permissions.add(permission_manage_products)
staff_api_client.user.user_permissions.add(permission_manage_checkouts)
staff_api_client.user.user_permissions.add(permission_manage_pages)
variables = {"event_type": event_type.name}
response = staff_api_client.post_graphql(query, variables=variables)
if not has_access:
Expand Down
25 changes: 25 additions & 0 deletions saleor/plugins/base_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from ..discount import DiscountInfo
from ..invoice.models import Invoice
from ..order.models import Fulfillment, Order, OrderLine
from ..page.models import Page
from ..product.models import (
Collection,
Product,
Expand Down Expand Up @@ -430,6 +431,30 @@ def checkout_updated(self, checkout: "Checkout", previous_value: Any) -> Any:
"""
return NotImplemented

def page_updated(self, page: "Page", previous_value: Any) -> Any:
"""Trigger when page is updated.
Overwrite this method if you need to trigger specific logic when a page is
updated.
"""
return NotImplemented

def page_created(self, page: "Page", previous_value: Any) -> Any:
"""Trigger when page is created.
Overwrite this method if you need to trigger specific logic when a page is
created.
"""
return NotImplemented

def page_deleted(self, page: "Page", previous_value: Any) -> Any:
"""Trigger when page is deleted.
Overwrite this method if you need to trigger specific logic when a page is
deleted.
"""
return NotImplemented

def fetch_taxes_data(self, previous_value: Any) -> Any:
"""Triggered when ShopFetchTaxRates mutation is called."""
return NotImplemented
Expand Down
13 changes: 13 additions & 0 deletions saleor/plugins/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from ..checkout.models import Checkout, CheckoutLine
from ..invoice.models import Invoice
from ..order.models import Fulfillment, Order, OrderLine
from ..page.models import Page
from ..payment.interface import (
CustomerSource,
GatewayResponse,
Expand Down Expand Up @@ -449,6 +450,18 @@ def checkout_updated(self, checkout: "Checkout"):
default_value = None
return self.__run_method_on_plugins("checkout_updated", default_value, checkout)

def page_created(self, page: "Page"):
default_value = None
return self.__run_method_on_plugins("page_created", default_value, page)

def page_updated(self, page: "Page"):
default_value = None
return self.__run_method_on_plugins("page_updated", default_value, page)

def page_deleted(self, page: "Page"):
default_value = None
return self.__run_method_on_plugins("page_deleted", default_value, page)

def initialize_payment(
self, gateway, payment_data: dict
) -> Optional["InitializedPaymentResponse"]:
Expand Down
20 changes: 20 additions & 0 deletions saleor/plugins/webhook/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
generate_fulfillment_payload,
generate_invoice_payload,
generate_order_payload,
generate_page_payload,
generate_product_payload,
)
from ..base_plugin import BasePlugin
Expand All @@ -17,6 +18,7 @@
from ...checkout.models import Checkout
from ...invoice.models import Invoice
from ...order.models import Fulfillment, Order
from ...page.models import Page
from ...product.models import Product


Expand Down Expand Up @@ -145,3 +147,21 @@ def checkout_updated(self, checkout: "Checkout", previous_value: Any) -> Any:
trigger_webhooks_for_event.delay(
WebhookEventType.CHECKOUT_UPADTED, checkout_data
)

def page_created(self, page: "Page", previous_value: Any) -> Any:
if not self.active:
return previous_value
page_data = generate_page_payload(page)
trigger_webhooks_for_event.delay(WebhookEventType.PAGE_CREATED, page_data)

def page_updated(self, page: "Page", previous_value: Any) -> Any:
if not self.active:
return previous_value
page_data = generate_page_payload(page)
trigger_webhooks_for_event.delay(WebhookEventType.PAGE_UPDATED, page_data)

def page_deleted(self, page: "Page", previous_value: Any) -> Any:
if not self.active:
return previous_value
page_data = generate_page_payload(page)
trigger_webhooks_for_event.delay(WebhookEventType.PAGE_DELETED, page_data)
Loading

0 comments on commit fbfc66e

Please sign in to comment.