mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-17 20:38:06 +00:00
Add EventGrid compatible webhook format (#1640)
This commit is contained in:
@ -21,6 +21,27 @@ Each event will be submitted via HTTP POST to the user provided URL.
|
||||
}
|
||||
```
|
||||
|
||||
## Event Grid Payload format
|
||||
|
||||
If webhook is set to have Event Grid message format then the payload will look as follows:
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"data": {
|
||||
"ping_id": "00000000-0000-0000-0000-000000000000"
|
||||
},
|
||||
"dataVersion": "1.0.0",
|
||||
"eventTime": "0001-01-01T00:00:00",
|
||||
"eventType": "ping",
|
||||
"id": "00000000-0000-0000-0000-000000000000",
|
||||
"subject": "example"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Event Types (EventType)
|
||||
|
||||
* [crash_reported](#crash_reported)
|
||||
|
@ -26,6 +26,21 @@ $ onefuzz webhooks create MYWEBHOOK https://contoso.com/my-custom-webhook task_c
|
||||
$
|
||||
```
|
||||
|
||||
Example creating a webhook subscription only the `task_created` events that produces webhook data in [Azure Event Grid](https://docs.microsoft.com/en-us/azure/event-grid/event-schema) compatible format:
|
||||
|
||||
```
|
||||
$ onefuzz webhooks create MYWEBHOOK https://contoso.com/my-custom-webhook task_created --message_format event_grid
|
||||
{
|
||||
"webhook_id": "cc6926de-7c6f-487e-96ec-7b632d3ed52b",
|
||||
"name": "MYWEBHOOK",
|
||||
"event_types": [
|
||||
"task_created"
|
||||
]
|
||||
}
|
||||
$
|
||||
```
|
||||
|
||||
|
||||
### Listing existing webhooks
|
||||
|
||||
```
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
import datetime
|
||||
import hmac
|
||||
import json
|
||||
import logging
|
||||
from hashlib import sha512
|
||||
from typing import List, Optional, Tuple
|
||||
@ -16,7 +17,11 @@ from onefuzztypes.enums import ErrorCode, WebhookMessageState
|
||||
from onefuzztypes.events import Event, EventMessage, EventPing, EventType
|
||||
from onefuzztypes.models import Error, Result
|
||||
from onefuzztypes.webhooks import Webhook as BASE_WEBHOOK
|
||||
from onefuzztypes.webhooks import WebhookMessage
|
||||
from onefuzztypes.webhooks import (
|
||||
WebhookMessage,
|
||||
WebhookMessageEventGrid,
|
||||
WebhookMessageFormat,
|
||||
)
|
||||
from onefuzztypes.webhooks import WebhookMessageLog as BASE_WEBHOOK_MESSAGE_LOG
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
@ -203,6 +208,7 @@ class Webhook(BASE_WEBHOOK, ORMMixin):
|
||||
event_type=message_log.event_type,
|
||||
event=message_log.event,
|
||||
secret_token=self.secret_token,
|
||||
message_format=self.message_format,
|
||||
)
|
||||
|
||||
headers = {"Content-type": "application/json", "User-Agent": USER_AGENT}
|
||||
@ -225,7 +231,24 @@ def build_message(
|
||||
event_type: EventType,
|
||||
event: Event,
|
||||
secret_token: Optional[str] = None,
|
||||
message_format: Optional[WebhookMessageFormat] = None,
|
||||
) -> Tuple[bytes, Optional[str]]:
|
||||
|
||||
if message_format and message_format == WebhookMessageFormat.event_grid:
|
||||
decoded = [
|
||||
json.loads(
|
||||
WebhookMessageEventGrid(
|
||||
id=event_id,
|
||||
data=event,
|
||||
dataVersion="1.0.0",
|
||||
subject=get_instance_name(),
|
||||
eventType=event_type,
|
||||
eventTime=datetime.datetime.now(datetime.timezone.utc),
|
||||
).json(sort_keys=True, exclude_none=True)
|
||||
)
|
||||
]
|
||||
data = json.dumps(decoded).encode()
|
||||
else:
|
||||
data = (
|
||||
WebhookMessage(
|
||||
webhook_id=webhook_id,
|
||||
|
@ -51,6 +51,9 @@ def post(req: func.HttpRequest) -> func.HttpResponse:
|
||||
event_types=request.event_types,
|
||||
secret_token=request.secret_token,
|
||||
)
|
||||
if request.message_format is not None:
|
||||
webhook.message_format = request.message_format
|
||||
|
||||
webhook.save()
|
||||
|
||||
webhook.url = None
|
||||
@ -83,6 +86,9 @@ def patch(req: func.HttpRequest) -> func.HttpResponse:
|
||||
if request.secret_token is not None:
|
||||
webhook.secret_token = request.secret_token
|
||||
|
||||
if request.message_format is not None:
|
||||
webhook.message_format = request.message_format
|
||||
|
||||
webhook.save()
|
||||
webhook.url = None
|
||||
webhook.secret_token = None
|
||||
|
@ -326,6 +326,7 @@ class Webhooks(Endpoint):
|
||||
event_types: List[events.EventType],
|
||||
*,
|
||||
secret_token: Optional[str] = None,
|
||||
message_format: Optional[webhooks.WebhookMessageFormat] = None,
|
||||
) -> webhooks.Webhook:
|
||||
"""Create a webhook"""
|
||||
self.logger.debug("creating webhook. name: %s", name)
|
||||
@ -333,7 +334,11 @@ class Webhooks(Endpoint):
|
||||
"POST",
|
||||
webhooks.Webhook,
|
||||
data=requests.WebhookCreate(
|
||||
name=name, url=url, event_types=event_types, secret_token=secret_token
|
||||
name=name,
|
||||
url=url,
|
||||
event_types=event_types,
|
||||
secret_token=secret_token,
|
||||
message_format=message_format,
|
||||
),
|
||||
)
|
||||
|
||||
@ -345,6 +350,7 @@ class Webhooks(Endpoint):
|
||||
url: Optional[str] = None,
|
||||
event_types: Optional[List[events.EventType]] = None,
|
||||
secret_token: Optional[str] = None,
|
||||
message_format: Optional[webhooks.WebhookMessageFormat] = None,
|
||||
) -> webhooks.Webhook:
|
||||
"""Update a webhook"""
|
||||
|
||||
@ -362,6 +368,7 @@ class Webhooks(Endpoint):
|
||||
url=url,
|
||||
event_types=event_types,
|
||||
secret_token=secret_token,
|
||||
message_format=message_format,
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import sys
|
||||
from typing import List, Optional
|
||||
from uuid import UUID
|
||||
@ -65,7 +67,7 @@ from onefuzztypes.models import (
|
||||
UserInfo,
|
||||
)
|
||||
from onefuzztypes.primitives import Container, PoolName, Region
|
||||
from onefuzztypes.webhooks import WebhookMessage
|
||||
from onefuzztypes.webhooks import WebhookMessage, WebhookMessageEventGrid
|
||||
|
||||
EMPTY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
ZERO_SHA256 = "0" * len(EMPTY_SHA256)
|
||||
@ -290,6 +292,25 @@ def main() -> None:
|
||||
instance_name="example",
|
||||
)
|
||||
|
||||
message_event_grid = WebhookMessageEventGrid(
|
||||
dataVersion="1.0.0",
|
||||
subject="example",
|
||||
eventType=EventType.ping,
|
||||
eventTime=datetime.datetime.min,
|
||||
id=UUID(int=0),
|
||||
data=EventPing(ping_id=UUID(int=0)),
|
||||
)
|
||||
|
||||
message_event_grid_json = json.dumps(
|
||||
[
|
||||
json.loads(
|
||||
message_event_grid.json(indent=4, exclude_none=True, sort_keys=True)
|
||||
)
|
||||
],
|
||||
indent=4,
|
||||
sort_keys=True,
|
||||
)
|
||||
|
||||
result = ""
|
||||
result += layer(
|
||||
1,
|
||||
@ -309,6 +330,21 @@ def main() -> None:
|
||||
message.json(indent=4, exclude_none=True, sort_keys=True),
|
||||
"json",
|
||||
)
|
||||
|
||||
result += layer(
|
||||
2,
|
||||
"Event Grid Payload format",
|
||||
"If webhook is set to have Event Grid message format then "
|
||||
"the payload will look as follows:",
|
||||
)
|
||||
|
||||
result += typed(
|
||||
3,
|
||||
"Example",
|
||||
message_event_grid_json,
|
||||
"json",
|
||||
)
|
||||
|
||||
result += layer(2, "Event Types (EventType)")
|
||||
|
||||
event_map = {get_event_type(x).name: x for x in examples}
|
||||
|
@ -22,6 +22,7 @@ from .enums import (
|
||||
from .events import EventType
|
||||
from .models import AutoScaleConfig, InstanceConfig, NotificationConfig
|
||||
from .primitives import Container, PoolName, Region
|
||||
from .webhooks import WebhookMessageFormat
|
||||
|
||||
|
||||
class BaseRequest(BaseModel):
|
||||
@ -211,6 +212,7 @@ class WebhookCreate(BaseRequest):
|
||||
url: AnyHttpUrl
|
||||
event_types: List[EventType]
|
||||
secret_token: Optional[str]
|
||||
message_format: Optional[WebhookMessageFormat]
|
||||
|
||||
|
||||
class WebhookSearch(BaseModel):
|
||||
@ -227,6 +229,7 @@ class WebhookUpdate(BaseModel):
|
||||
event_types: Optional[List[EventType]]
|
||||
url: Optional[AnyHttpUrl]
|
||||
secret_token: Optional[str]
|
||||
message_format: Optional[WebhookMessageFormat]
|
||||
|
||||
|
||||
class NodeAddSshKey(BaseModel):
|
||||
|
@ -3,19 +3,35 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import List, Optional
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from pydantic import AnyHttpUrl, BaseModel, Field
|
||||
|
||||
from .enums import WebhookMessageState
|
||||
from .events import EventMessage, EventType
|
||||
from .events import Event, EventMessage, EventType
|
||||
|
||||
|
||||
class WebhookMessageFormat(Enum):
|
||||
onefuzz = "onefuzz"
|
||||
event_grid = "event_grid"
|
||||
|
||||
|
||||
class WebhookMessage(EventMessage):
|
||||
webhook_id: UUID
|
||||
|
||||
|
||||
class WebhookMessageEventGrid(BaseModel):
|
||||
dataVersion: str
|
||||
subject: str
|
||||
eventType: EventType
|
||||
eventTime: datetime
|
||||
id: UUID
|
||||
data: Event
|
||||
|
||||
|
||||
class WebhookMessageLog(WebhookMessage):
|
||||
state: WebhookMessageState = Field(default=WebhookMessageState.queued)
|
||||
try_count: int = Field(default=0)
|
||||
@ -27,3 +43,4 @@ class Webhook(BaseModel):
|
||||
url: Optional[AnyHttpUrl]
|
||||
event_types: List[EventType]
|
||||
secret_token: Optional[str]
|
||||
message_format: Optional[WebhookMessageFormat]
|
||||
|
Reference in New Issue
Block a user