mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 17:02:49 +03:00
docs: Event Trigger handler for order-status-change notifications fixes #9907
GITHUB_PR_NUMBER: 9946 GITHUB_PR_URL: https://github.com/hasura/graphql-engine/pull/9946 PR-URL: https://github.com/hasura/graphql-engine-mono/pull/10399 Co-authored-by: Harshal <37841724+harshalkh@users.noreply.github.com> Co-authored-by: Rob Dominguez <24390149+robertjdominguez@users.noreply.github.com> GitOrigin-RevId: 04669c247b6a5bed9d4bb463e6c7e7b4392e8fed
This commit is contained in:
parent
3f48449771
commit
911b237dcb
@ -41,3 +41,9 @@ the quality of your content and reduce the amount of time spent on manual review
|
|||||||
This recipe shows how to use Hasura's Event Triggers to automatically send a welcome email to a new user when they sign
|
This recipe shows how to use Hasura's Event Triggers to automatically send a welcome email to a new user when they sign
|
||||||
up. We'll build a webhook that parses Event Trigger data, sends an email, and then performs operations on our data based
|
up. We'll build a webhook that parses Event Trigger data, sends an email, and then performs operations on our data based
|
||||||
on the results. [Check it out](/event-triggers/recipes/new-user-welcome.mdx)!
|
on the results. [Check it out](/event-triggers/recipes/new-user-welcome.mdx)!
|
||||||
|
|
||||||
|
### Send a Notification when Order Status Changes
|
||||||
|
|
||||||
|
This recipe shows how to use Hasura's Event Triggers to automatically sends a notification to user when a status of their order changes.
|
||||||
|
We'll build a webhook that parses Event Trigger data and sends a notification using data received in event. [Check it out](/event-triggers/recipes/order-status-notification.mdx)!
|
||||||
|
|
||||||
|
314
docs/docs/event-triggers/recipes/order-status-notification.mdx
Normal file
314
docs/docs/event-triggers/recipes/order-status-notification.mdx
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
---
|
||||||
|
title: Using Event Triggers to send order-status-change notifications.
|
||||||
|
description: Succinct, tested, and reusable code recipes for common use cases in Hasura.
|
||||||
|
sidebar_label: Send a Notification when a Value Changes
|
||||||
|
keywords:
|
||||||
|
- hasura
|
||||||
|
- docs
|
||||||
|
- recipes
|
||||||
|
- event triggers
|
||||||
|
- order
|
||||||
|
- status change
|
||||||
|
- notifications
|
||||||
|
sidebar_position: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
import Tabs from '@theme/Tabs';
|
||||||
|
import TabItem from '@theme/TabItem';
|
||||||
|
import Thumbnail from '@site/src/components/Thumbnail';
|
||||||
|
import SampleAppBlock from '@site/src/components/SampleAppBlock';
|
||||||
|
|
||||||
|
# Send a Notification when a Value Changes
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Using Event Triggers allows you to call a webhook with a contextual payload whenever a specific event occurs in your
|
||||||
|
database. In this recipe, we'll create an Event Trigger that will fire whenever the status of an order changes. We'll
|
||||||
|
then send a notification to that user.
|
||||||
|
|
||||||
|
<SampleAppBlock dependent />
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before getting started, ensure that you have the following in place:
|
||||||
|
|
||||||
|
- The docs e-commerce sample app deployed to Hasura Cloud.
|
||||||
|
|
||||||
|
:::info Tunneling your webhook endpoint from your local machine
|
||||||
|
|
||||||
|
If you plan on using a webhook endpoint hosted on your own machine, ensure that you have a tunneling service such as
|
||||||
|
[ngrok](https://ngrok.com/) set up so that your Cloud Project can communicate with your local machine.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Our model
|
||||||
|
|
||||||
|
Event Triggers are designed to run when specific operations occur on a table, such as insertions, updates, and
|
||||||
|
deletions. When architecting your own Event Trigger, you need to consider the following:
|
||||||
|
|
||||||
|
- Which table's changes will initiate the Event Trigger?
|
||||||
|
- Which operation(s) on that table will initiate the Event Trigger?
|
||||||
|
- What should my webhook do with the data it receives?
|
||||||
|
|
||||||
|
## Step 1: Create the Event Trigger
|
||||||
|
|
||||||
|
Head to the `Events` tab of the Hasura Console and click `Create`:
|
||||||
|
|
||||||
|
<Thumbnail
|
||||||
|
src="/img/event-triggers/recipes/review-moderation/click-create-event-trigger.png"
|
||||||
|
alt="Click Create"
|
||||||
|
width="1000px"
|
||||||
|
/>
|
||||||
|
|
||||||
|
## Step 2: Configure the Event Trigger
|
||||||
|
|
||||||
|
First, provide a name for your trigger, e.g., `order_status_change`. Choose the `public` schema and the `orders` table.
|
||||||
|
Then, select the `update` operation under `Trigger Operations`. Then, select the `status` column under
|
||||||
|
`Listen columns for update`.
|
||||||
|
|
||||||
|
Finally, enter a webhook URL that will be called when the event is triggered. This webhook will be responsible for
|
||||||
|
parsing the body of the request and sending the notification to the user to whom order belongs; it can be hosted
|
||||||
|
anywhere, and written in any language you like.
|
||||||
|
|
||||||
|
The route on our webhook we'll use is `/order-status-change`. Below, we'll see what this looks like with a service like
|
||||||
|
[ngrok](https://ngrok.com/), but the format will follow this template:
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://<your-webhook-url>/order-status-change
|
||||||
|
```
|
||||||
|
|
||||||
|
:::info Tunneling your webhook endpoint
|
||||||
|
|
||||||
|
Since our project is running on Hasura Cloud, and our handler will run on our local machine, we'll use ngrok to expose
|
||||||
|
the webhook endpoint to the internet. This will allow us to expose a public URL that will forward requests to our local
|
||||||
|
machine and the server we'll configure below.
|
||||||
|
|
||||||
|
You'll need to modify your webhook URL to use the public URL provided by ngrok.
|
||||||
|
|
||||||
|
After installing ngrok and
|
||||||
|
[authenticating](https://ngrok.com/docs/secure-tunnels/ngrok-agent/tunnel-authtokens/#:~:text=Once%20you've%20signed%20up,make%20installing%20the%20authtoken%20simple.),
|
||||||
|
you can do this by running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok http 4000
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, copy the `Forwarding` value for use in our webhook 🎉
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
Under `Advanced Settings`, we can configure the headers that will be sent with the request. We'll add an
|
||||||
|
`authentication` header to prevent abuse of the endpoint and ensure that only Hasura can trigger the event. Set the
|
||||||
|
`Key` as `secret-authorization-string` and the `Value` as `super_secret_string_123`:
|
||||||
|
|
||||||
|
<Thumbnail
|
||||||
|
src="/img/event-triggers/recipes/review-moderation/event-trigger-secret-header.png"
|
||||||
|
alt="Add the secret header"
|
||||||
|
width="1000px"
|
||||||
|
/>
|
||||||
|
|
||||||
|
Before exiting, open the `Add Request Options Transform` section and check `POST`. Then, click `Create Event Trigger`.
|
||||||
|
|
||||||
|
## Step 3: Create a webhook to handle the request
|
||||||
|
|
||||||
|
Whenever a status is changed in the `orders` table, the Event Trigger fires. Hasura will send a request to the webhook
|
||||||
|
URL you provided. In this example, we're simply going to send a `POST` request. Our webhook will parse the request,
|
||||||
|
ensure the header is correct, and then send a notification to user in our application.
|
||||||
|
|
||||||
|
Event Triggers sent by Hasura to your webhook as a request include a [payload](/event-triggers/payload.mdx) with `event`
|
||||||
|
data nested inside the `body` object of the request. This `event` object can then be parsed and values extracted from it
|
||||||
|
to be used in your webhook.
|
||||||
|
|
||||||
|
Below, we've written an example of webhook. As we established earlier, this runs on port `4000`. If you're attempting to
|
||||||
|
run this locally, follow the instructions below. If you're running this in a hosted environment, use this code as a
|
||||||
|
guide to write your own webhook.
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
defaultValue="javascript"
|
||||||
|
values={[
|
||||||
|
{ label: 'JavaScript', value: 'javascript' },
|
||||||
|
{ label: 'Python', value: 'python' },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<TabItem value="javascript">
|
||||||
|
|
||||||
|
Init a new project with `npm init` and install the following dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install express body-parser
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>
|
||||||
|
Then, create a new file called <code>index.js</code> and add the following code:
|
||||||
|
</summary>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const express = require('express');
|
||||||
|
const bodyParser = require('body-parser');
|
||||||
|
|
||||||
|
// get express sorted
|
||||||
|
const app = express();
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
|
|
||||||
|
// notification for user about change in status of order
|
||||||
|
async function sendNotification(userId, orderId, orderStatus) {
|
||||||
|
const response = await fetch(`<YOUR_PROJECT_ENDPOINT>`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'x-hasura-admin-secret': `<YOUR_ADMIN_SECRET>`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
query: `
|
||||||
|
mutation InsertNotification($user_id: uuid!, $message: String!) {
|
||||||
|
insert_notifications_one(object: {user_id: $user_id, message: $message}) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
variables: {
|
||||||
|
user_id: userId,
|
||||||
|
message: `Status of Order #${orderId} is now ${orderStatus}.`,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
console.log(
|
||||||
|
`Notification sent. The user has received the following notification: Status of Order #${orderId} is now ${orderStatus}.`
|
||||||
|
);
|
||||||
|
const { data } = await response.json();
|
||||||
|
return data.insert_notifications_one;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now we can write the actual handler using functions
|
||||||
|
app.post('/order-status-change', async (req, res) => {
|
||||||
|
// check the header for auth
|
||||||
|
const authHeader = req.headers['secret-authorization-string'];
|
||||||
|
if (authHeader !== 'super_secret_string_123') {
|
||||||
|
return res.status(401).json({
|
||||||
|
message: 'Unauthorized',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the status from the event payload
|
||||||
|
const orderStatus = req.body.event.data.new.status;
|
||||||
|
const orderId = req.body.event.data.new.id;
|
||||||
|
const userId = req.body.event.data.new.user_id;
|
||||||
|
|
||||||
|
// send notification to user
|
||||||
|
await sendNotification(userId, orderId, orderStatus);
|
||||||
|
|
||||||
|
// send some JSON to the client
|
||||||
|
res.json({
|
||||||
|
orderId: orderId,
|
||||||
|
orderStatus: orderStatus,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// start the server
|
||||||
|
app.listen(4000, () => {
|
||||||
|
console.log('Server started on port 4000');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
You can run the server by running `node index.js` in your terminal.
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="python">
|
||||||
|
|
||||||
|
Make sure you have the necessary dependencies installed. You can use pip to install them:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install Flask
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>
|
||||||
|
Then, create a new file called <code>index.py</code> and add the following code:
|
||||||
|
</summary>
|
||||||
|
|
||||||
|
```python
|
||||||
|
from flask import Flask, request, jsonify
|
||||||
|
import requests
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# Hasura and OpenAI config
|
||||||
|
config = {
|
||||||
|
'url': '<YOUR_PROJECT_ENDPOINT>',
|
||||||
|
'secret': '<YOUR_ADMIN_SECRET>',
|
||||||
|
}
|
||||||
|
|
||||||
|
def sendNotification(user_id, order_id, order_status):
|
||||||
|
#REST Api call to HASURA
|
||||||
|
response = requests.post(
|
||||||
|
config["url"],
|
||||||
|
json={
|
||||||
|
"query": """
|
||||||
|
mutation InsertNotification($user_id: uuid!, $message: String!) {
|
||||||
|
insert_notifications_one(object: {user_id: $user_id, message: $message}) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
"variables": {
|
||||||
|
"user_id": user_id,
|
||||||
|
"message": f"Status of Order #{order_id} is now {order_status}.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
headers={
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"x-hasura-admin-secret": config["secret"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
print(f"Notification sent. The user has received the following notification: Status of Order #{order_id} is now {order_status}.")
|
||||||
|
data = response.json()
|
||||||
|
return data.get("insert_notifications_one", None)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/order-status-change', methods=['POST'])
|
||||||
|
def order_status_change():
|
||||||
|
# Confirm the authentication header is correct
|
||||||
|
auth_header = request.headers.get('secret-authorization-string')
|
||||||
|
if auth_header != 'super_secret_string_123':
|
||||||
|
return jsonify({'message': 'Unauthorized'}), 401
|
||||||
|
|
||||||
|
# Get the user's name and email from the request body
|
||||||
|
data = request.get_json()
|
||||||
|
order_status = data['event']['data']['new']['status']
|
||||||
|
order_id = data['event']['data']['new']['id']
|
||||||
|
user_id = data['event']['data']['new']['user_id']
|
||||||
|
|
||||||
|
sendNotification(user_id, order_id, order_status)
|
||||||
|
|
||||||
|
return jsonify({'message': 'Order Change Notification sent!'})
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(port=4000)
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
You can run the server by running `python index.py` in your terminal.
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
If you see the message `Webhook server is running on port 4000`, you're good to go!
|
||||||
|
|
||||||
|
## Step 4: Test the setup
|
||||||
|
|
||||||
|
Head to the `Data` tab and modify one of the orders. Change the `status` to `delivered` and click `Save`. You should see
|
||||||
|
your server log the following:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Notification sent. The user has received the following notification: Status of Order #<ORDER_ID> is now delivered.
|
||||||
|
```
|
||||||
|
|
||||||
|
We can then head to our `notifications` table in the `Data` tab and we'll also see the new notification 🎉
|
||||||
|
|
||||||
|
Feel free to customize the webhook implementation based on your specific requirements. Remember to handle error
|
||||||
|
scenarios, implement necessary validations, and add appropriate security measures to your webhook endpoint.
|
Loading…
Reference in New Issue
Block a user