Skip to content

Commit f5c7f6a

Browse files
f-wrightAuto-format Bot
and
Auto-format Bot
authored
Add headers to webhook alerts (#320)
This PR allows the user to customize the headers field for their webhook alerts in addition to the payload. This will enable auth, which is required by a lot of different platforms. --------- Co-authored-by: Auto-format Bot <[email protected]>
1 parent 401bd14 commit f5c7f6a

File tree

9 files changed

+75
-6
lines changed

9 files changed

+75
-6
lines changed

Diff for: generated/.openapi-generator/FILES

-2
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,4 @@ setup.cfg
142142
setup.py
143143
test-requirements.txt
144144
test/__init__.py
145-
test/test_payload_template.py
146-
test/test_payload_template_request.py
147145
tox.ini

Diff for: generated/docs/PayloadTemplate.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
Name | Type | Description | Notes
66
------------ | ------------- | ------------- | -------------
77
**template** | **str** | |
8+
**headers** | **{str: (str,)}, none_type** | | [optional]
89
**any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional]
910

1011
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

Diff for: generated/docs/PayloadTemplateRequest.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
Name | Type | Description | Notes
66
------------ | ------------- | ------------- | -------------
77
**template** | **str** | |
8+
**headers** | **{str: (str,)}, none_type** | | [optional]
89
**any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional]
910

1011
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

Diff for: generated/groundlight_openapi_client/model/payload_template.py

+7
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ def openapi_types():
8989
"""
9090
return {
9191
"template": (str,), # noqa: E501
92+
"headers": (
93+
{str: (str,)},
94+
none_type,
95+
), # noqa: E501
9296
}
9397

9498
@cached_property
@@ -97,6 +101,7 @@ def discriminator():
97101

98102
attribute_map = {
99103
"template": "template", # noqa: E501
104+
"headers": "headers", # noqa: E501
100105
}
101106

102107
read_only_vars = {}
@@ -142,6 +147,7 @@ def _from_openapi_data(cls, template, *args, **kwargs): # noqa: E501
142147
Animal class but this time we won't travel
143148
through its discriminator because we passed in
144149
_visited_composed_classes = (Animal,)
150+
headers ({str: (str,)}, none_type): [optional] # noqa: E501
145151
"""
146152

147153
_check_type = kwargs.pop("_check_type", True)
@@ -230,6 +236,7 @@ def __init__(self, template, *args, **kwargs): # noqa: E501
230236
Animal class but this time we won't travel
231237
through its discriminator because we passed in
232238
_visited_composed_classes = (Animal,)
239+
headers ({str: (str,)}, none_type): [optional] # noqa: E501
233240
"""
234241

235242
_check_type = kwargs.pop("_check_type", True)

Diff for: generated/groundlight_openapi_client/model/payload_template_request.py

+7
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ def openapi_types():
9393
"""
9494
return {
9595
"template": (str,), # noqa: E501
96+
"headers": (
97+
{str: (str,)},
98+
none_type,
99+
), # noqa: E501
96100
}
97101

98102
@cached_property
@@ -101,6 +105,7 @@ def discriminator():
101105

102106
attribute_map = {
103107
"template": "template", # noqa: E501
108+
"headers": "headers", # noqa: E501
104109
}
105110

106111
read_only_vars = {}
@@ -146,6 +151,7 @@ def _from_openapi_data(cls, template, *args, **kwargs): # noqa: E501
146151
Animal class but this time we won't travel
147152
through its discriminator because we passed in
148153
_visited_composed_classes = (Animal,)
154+
headers ({str: (str,)}, none_type): [optional] # noqa: E501
149155
"""
150156

151157
_check_type = kwargs.pop("_check_type", True)
@@ -234,6 +240,7 @@ def __init__(self, template, *args, **kwargs): # noqa: E501
234240
Animal class but this time we won't travel
235241
through its discriminator because we passed in
236242
_visited_composed_classes = (Animal,)
243+
headers ({str: (str,)}, none_type): [optional] # noqa: E501
237244
"""
238245

239246
_check_type = kwargs.pop("_check_type", True)

Diff for: generated/model.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# generated by datamodel-codegen:
22
# filename: public-api.yaml
3-
# timestamp: 2025-02-24T22:54:15+00:00
3+
# timestamp: 2025-02-25T19:28:25+00:00
44

55
from __future__ import annotations
66

@@ -111,10 +111,12 @@ class NoteRequest(BaseModel):
111111

112112
class PayloadTemplate(BaseModel):
113113
template: str
114+
headers: Optional[Dict[str, str]] = None
114115

115116

116117
class PayloadTemplateRequest(BaseModel):
117118
template: constr(min_length=1)
119+
headers: Optional[Dict[str, constr(min_length=1)]] = None
118120

119121

120122
class ROI(BaseModel):

Diff for: spec/public-api.yaml

+11
Original file line numberDiff line numberDiff line change
@@ -1343,6 +1343,11 @@ components:
13431343
properties:
13441344
template:
13451345
type: string
1346+
headers:
1347+
type: object
1348+
additionalProperties:
1349+
type: string
1350+
nullable: true
13461351
required:
13471352
- template
13481353
PayloadTemplateRequest:
@@ -1351,6 +1356,12 @@ components:
13511356
template:
13521357
type: string
13531358
minLength: 1
1359+
headers:
1360+
type: object
1361+
additionalProperties:
1362+
type: string
1363+
minLength: 1
1364+
nullable: true
13541365
required:
13551366
- template
13561367
ROI:

Diff for: src/groundlight/experimental_api.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,11 @@ def make_webhook_action(
188188
payload_template=payload_template,
189189
)
190190

191-
def make_payload_template(self, template: str) -> PayloadTemplate:
191+
def make_payload_template(self, template: str, headers: Optional[Dict[str, str]] = None) -> PayloadTemplate:
192192
"""
193193
Creates a PayloadTemplate object for use in creating alerts
194194
"""
195-
return PayloadTemplate(template=template)
195+
return PayloadTemplate(template=template, headers=headers)
196196

197197
def create_alert( # pylint: disable=too-many-locals, too-many-arguments # noqa: PLR0913
198198
self,
@@ -282,7 +282,10 @@ def create_alert( # pylint: disable=too-many-locals, too-many-arguments # noqa
282282
url=str(webhook_action.url),
283283
include_image=webhook_action.include_image,
284284
payload_template=(
285-
PayloadTemplateRequest(template=webhook_action.payload_template.template)
285+
PayloadTemplateRequest(
286+
template=webhook_action.payload_template.template,
287+
headers=webhook_action.payload_template.headers,
288+
)
286289
if webhook_action.payload_template
287290
else None
288291
),

Diff for: test/unit/test_actions.py

+39
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,42 @@ def test_create_alert_webhook_action_with_invalid_payload_template(gl_experiment
154154
with pytest.raises(ApiException) as e:
155155
gl_experimental.create_alert(det, f"test_alert_{name}", condition, webhook_actions=webhook_action)
156156
assert e.value.status == bad_request_exception_status_code
157+
158+
159+
def test_create_alert_webhook_action_headers(gl_experimental: ExperimentalApi):
160+
name = f"Test {datetime.utcnow()}"
161+
det = gl_experimental.get_or_create_detector(name, "test_query")
162+
condition = gl_experimental.make_condition("ANSWERED_CONSECUTIVELY", {"num_consecutive_labels": 1, "label": "YES"})
163+
164+
test_api_key = "test_api_key"
165+
url = "https://example.com/webhook"
166+
headers = {
167+
"Authorization": f"Bearer {test_api_key}",
168+
}
169+
170+
template = """{"records": [{"fields": {"detector_id": "{{ detector_id }}" } }]}"""
171+
172+
payload_template = {"template": template, "headers": headers}
173+
webhook_action = gl_experimental.make_webhook_action(
174+
url=url, include_image=False, payload_template=payload_template
175+
)
176+
177+
alert = gl_experimental.create_alert(
178+
det,
179+
f"test_alert_{name}",
180+
condition,
181+
webhook_actions=webhook_action,
182+
)
183+
184+
assert len(alert.webhook_action) == 1
185+
assert alert.webhook_action[0].payload_template.template == template
186+
assert alert.webhook_action[0].payload_template.headers == headers
187+
188+
189+
def test_create_invalid_payload_template_headers(gl_experimental: ExperimentalApi):
190+
with pytest.raises(Exception) as e:
191+
gl_experimental.make_payload_template(
192+
'{"template": "This is a fine template"}', headers="bad headers" # type: ignore
193+
)
194+
assert e.typename == "ValidationError"
195+
assert "Input should be a valid dictionary" in str(e.value)

0 commit comments

Comments
 (0)