aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--SCHEMA.md22
-rw-r--r--backend/constants.py1
-rw-r--r--backend/models/form.py51
3 files changed, 67 insertions, 7 deletions
diff --git a/SCHEMA.md b/SCHEMA.md
index b37ed1d..894ffec 100644
--- a/SCHEMA.md
+++ b/SCHEMA.md
@@ -12,13 +12,14 @@ In this document:
## Form
-| Field | Type | Description | Example |
-| ----------- | ---------------------------------------- | ----------------------------------------------------------------------------------------- | --------------------------- |
-| `id` | Unique identifier | A user selected, unique, descriptive identifier (used in URL routes, so no spaces) | `"ban-appeals"` |
-| `features` | List of [form features](#form-features) | A list of features to change the behaviour of the form, described in the features section | `["OPEN", "COLLECT_EMAIL"]` |
-| `questions` | List of [form questions](#form-question) | The list of questions to render on a specific form | Too long! See below |
-| `name` | String | Name of the form | `"Summer Code Jam 2100"` |
-| `description` | String | Form description | `"This is my amazing form description."` |
+| Field | Type | Description | Example |
+| ------------- | ----------------------------------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------- |
+| `id` | Unique identifier | A user selected, unique, descriptive identifier (used in URL routes, so no spaces) | `"ban-appeals"` |
+| `features` | List of [form features](#form-features) | A list of features to change the behaviour of the form, described in the features section | `["OPEN", "COLLECT_EMAIL"]` |
+| `meta` | Mapping of [meta options](#meta-options) | Meta properties for the form. | See meta-options section |
+| `questions` | List of [form questions](#form-question) | The list of questions to render on a specific form | Too long! See below |
+| `name` | String | Name of the form | `"Summer Code Jam 2100"` |
+| `description` | String | Form description | `"This is my amazing form description."` |
### Form features
@@ -29,6 +30,13 @@ In this document:
| `OPEN` | The form is currently accepting responses. |
| `COLLECT_EMAIL` | The form should collect the email from submissions. Requires `REQUIRES_LOGIN` |
| `DISABLE_ANTISPAM` | Disable the anti-spam checks from running on a form submission. |
+| `WEBHOOK_ENABLED` | The form should notify the webhook. Has no effect if no webhook is set. |
+
+### Meta options
+| Field | Description | Example |
+| --------- | ----------------------------------- | ------------------------------------------------------------------------------------------ |
+| `webhook` | Mapping of webhook url and message. | `"webhook": {"url": "https://discord.com/api/webhooks/id/key", "message": "Hello World!"}` |
+
### Form question
diff --git a/backend/constants.py b/backend/constants.py
index fdf7092..e733e53 100644
--- a/backend/constants.py
+++ b/backend/constants.py
@@ -58,3 +58,4 @@ class FormFeatures(Enum):
OPEN = "OPEN"
COLLECT_EMAIL = "COLLECT_EMAIL"
DISABLE_ANTISPAM = "DISABLE_ANTISPAM"
+ WEBHOOK_ENABLED = "WEBHOOK_ENABLED"
diff --git a/backend/models/form.py b/backend/models/form.py
index cb58065..e6c56a4 100644
--- a/backend/models/form.py
+++ b/backend/models/form.py
@@ -1,5 +1,6 @@
import typing as t
+import httpx
from pydantic import BaseModel, Field, validator
from backend.constants import FormFeatures
@@ -8,6 +9,55 @@ from .question import Question
PUBLIC_FIELDS = ["id", "features", "questions", "name", "description"]
+class _WebHook(BaseModel):
+ """Schema model of discord webhooks."""
+ url: str
+ message: str
+
+ @validator("url")
+ def validate_url(cls, url: str) -> str:
+ """Checks if discord webhook urls are valid."""
+ if not isinstance(url, str):
+ raise ValueError("Webhook URL must be a string.")
+
+ if "discord.com/api/webhooks/" not in url:
+ raise ValueError("URL must be a discord webhook.")
+
+ # Attempt to connect to URL
+ try:
+ httpx.get(url).raise_for_status()
+
+ except httpx.RequestError as e:
+ # Catch exceptions in request format
+ raise ValueError(
+ f"Encountered error while trying to connect to url: `{e}`"
+ )
+
+ except httpx.HTTPStatusError as e:
+ # Catch exceptions in response
+ status = e.response.status_code
+
+ if status == 401:
+ raise ValueError(
+ "Could not authenticate with target. Please check the webhook url."
+ )
+ elif status == 404:
+ raise ValueError(
+ "Target could not find webhook url. Please check the webhook url."
+ )
+ else:
+ raise ValueError(
+ f"Unknown error ({status}) while connecting to target: {e}"
+ )
+
+ return url
+
+
+class _FormMeta(BaseModel):
+ """Schema model for form meta data."""
+ webhook: _WebHook
+
+
class Form(BaseModel):
"""Schema model for form."""
@@ -16,6 +66,7 @@ class Form(BaseModel):
questions: list[Question]
name: str
description: str
+ meta: _FormMeta
class Config:
allow_population_by_field_name = True