mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-02 13:45:10 -05:00
Enhancement: allow specifying JSON encoding for webhooks (#8799)
This commit is contained in:
parent
cd50f20a20
commit
ed1775e689
@ -419,6 +419,7 @@ The following workflow action types are available:
|
||||
|
||||
- The URL to send the request to
|
||||
- The request body as text or as key-value pairs, which can include placeholders, see [placeholders](usage.md#workflow-placeholders) below.
|
||||
- Encoding for the request body, either JSON or form data
|
||||
- The request headers as key-value pairs
|
||||
|
||||
#### Workflow placeholders
|
||||
|
@ -338,7 +338,10 @@
|
||||
<input type="hidden" formControlName="id" />
|
||||
<div class="col">
|
||||
<pngx-input-text i18n-title title="Webhook url" formControlName="url" [error]="error?.actions?.[i]?.url"></pngx-input-text>
|
||||
<pngx-input-switch i18n-title title="Use parameters for webhook body" formControlName="use_params"></pngx-input-switch>
|
||||
<div class="d-flex">
|
||||
<pngx-input-switch i18n-title title="Use parameters for webhook body" formControlName="use_params" [horizontal]="true"></pngx-input-switch>
|
||||
<pngx-input-switch i18n-title title="Send webhook payload as JSON" formControlName="as_json" [horizontal]="true" class="ms-5"></pngx-input-switch>
|
||||
</div>
|
||||
@if (formGroup.get('webhook').value['use_params']) {
|
||||
<pngx-input-entries i18n-title title="Webhook params" formControlName="params" [error]="error?.actions?.[i]?.params"></pngx-input-entries>
|
||||
} @else {
|
||||
|
@ -471,6 +471,7 @@ export class WorkflowEditDialogComponent
|
||||
id: new FormControl(action.webhook?.id),
|
||||
url: new FormControl(action.webhook?.url),
|
||||
use_params: new FormControl(action.webhook?.use_params),
|
||||
as_json: new FormControl(action.webhook?.as_json),
|
||||
params: new FormControl(action.webhook?.params),
|
||||
body: new FormControl(action.webhook?.body),
|
||||
headers: new FormControl(action.webhook?.headers),
|
||||
@ -588,6 +589,7 @@ export class WorkflowEditDialogComponent
|
||||
id: null,
|
||||
url: null,
|
||||
use_params: true,
|
||||
as_json: false,
|
||||
params: null,
|
||||
body: null,
|
||||
headers: null,
|
||||
|
@ -22,6 +22,8 @@ export interface WorkflowActionWebhook extends ObjectWithId {
|
||||
|
||||
use_params?: boolean
|
||||
|
||||
as_json?: boolean
|
||||
|
||||
params?: object
|
||||
|
||||
body?: string
|
||||
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.1.4 on 2025-01-18 19:35
|
||||
|
||||
from django.db import migrations
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("documents", "1060_alter_customfieldinstance_value_select"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="workflowactionwebhook",
|
||||
name="as_json",
|
||||
field=models.BooleanField(default=False, verbose_name="send as JSON"),
|
||||
),
|
||||
]
|
@ -1209,6 +1209,11 @@ class WorkflowActionWebhook(models.Model):
|
||||
verbose_name=_("use parameters"),
|
||||
)
|
||||
|
||||
as_json = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name=_("send as JSON"),
|
||||
)
|
||||
|
||||
params = models.JSONField(
|
||||
_("webhook parameters"),
|
||||
null=True,
|
||||
|
@ -1876,6 +1876,7 @@ class WorkflowActionWebhookSerializer(serializers.ModelSerializer):
|
||||
"id",
|
||||
"url",
|
||||
"use_params",
|
||||
"as_json",
|
||||
"params",
|
||||
"body",
|
||||
"headers",
|
||||
|
@ -573,14 +573,29 @@ def run_workflows_updated(sender, document: Document, logging_group=None, **kwar
|
||||
max_retries=3,
|
||||
throws=(httpx.HTTPError,),
|
||||
)
|
||||
def send_webhook(url, data, headers, files):
|
||||
def send_webhook(
|
||||
url: str,
|
||||
data: str | dict,
|
||||
headers: dict,
|
||||
files: dict,
|
||||
*,
|
||||
as_json: bool = False,
|
||||
):
|
||||
try:
|
||||
httpx.post(
|
||||
url,
|
||||
data=data,
|
||||
files=files,
|
||||
headers=headers,
|
||||
).raise_for_status()
|
||||
if as_json:
|
||||
httpx.post(
|
||||
url,
|
||||
json=data,
|
||||
files=files,
|
||||
headers=headers,
|
||||
).raise_for_status()
|
||||
else:
|
||||
httpx.post(
|
||||
url,
|
||||
data=data,
|
||||
files=files,
|
||||
headers=headers,
|
||||
).raise_for_status()
|
||||
logger.info(
|
||||
f"Webhook sent to {url}",
|
||||
)
|
||||
@ -1092,6 +1107,7 @@ def run_workflows(
|
||||
data=data,
|
||||
headers=headers,
|
||||
files=files,
|
||||
as_json=action.webhook.as_json,
|
||||
)
|
||||
logger.debug(
|
||||
f"Webhook to {action.webhook.url} queued",
|
||||
|
@ -11,6 +11,7 @@ from guardian.shortcuts import assign_perm
|
||||
from guardian.shortcuts import get_groups_with_perms
|
||||
from guardian.shortcuts import get_users_with_perms
|
||||
from httpx import HTTPStatusError
|
||||
from pytest_httpx import HTTPXMock
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from documents.signals.handlers import run_workflows
|
||||
@ -2407,6 +2408,7 @@ class TestWorkflows(
|
||||
data=f"Test message: http://localhost:8000/documents/{doc.id}/",
|
||||
headers={},
|
||||
files=None,
|
||||
as_json=False,
|
||||
)
|
||||
|
||||
@override_settings(
|
||||
@ -2468,6 +2470,7 @@ class TestWorkflows(
|
||||
data=f"Test message: http://localhost:8000/documents/{doc.id}/",
|
||||
headers={},
|
||||
files={"file": ("simple.pdf", mock.ANY, "application/pdf")},
|
||||
as_json=False,
|
||||
)
|
||||
|
||||
@override_settings(
|
||||
@ -2669,3 +2672,43 @@ class TestWorkflows(
|
||||
)
|
||||
|
||||
mock_post.assert_called_once()
|
||||
|
||||
|
||||
class TestWebhookSend:
|
||||
def test_send_webhook_data_or_json(
|
||||
self,
|
||||
httpx_mock: HTTPXMock,
|
||||
):
|
||||
"""
|
||||
GIVEN:
|
||||
- Nothing
|
||||
WHEN:
|
||||
- send_webhook is called with data or dict
|
||||
THEN:
|
||||
- data is sent as form-encoded and json, respectively
|
||||
"""
|
||||
httpx_mock.add_response(
|
||||
content=b"ok",
|
||||
)
|
||||
|
||||
send_webhook(
|
||||
url="http://paperless-ngx.com",
|
||||
data="Test message",
|
||||
headers={},
|
||||
files=None,
|
||||
as_json=False,
|
||||
)
|
||||
assert httpx_mock.get_request().headers.get("Content-Type") is None
|
||||
httpx_mock.reset()
|
||||
|
||||
httpx_mock.add_response(
|
||||
json={"status": "ok"},
|
||||
)
|
||||
send_webhook(
|
||||
url="http://paperless-ngx.com",
|
||||
data={"message": "Test message"},
|
||||
headers={},
|
||||
files=None,
|
||||
as_json=True,
|
||||
)
|
||||
assert httpx_mock.get_request().headers["Content-Type"] == "application/json"
|
||||
|
Loading…
x
Reference in New Issue
Block a user