-
Notifications
You must be signed in to change notification settings - Fork 165
feat(BA-3635): Implement Email Notification Channel #7941
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
4df76e1 to
3901df4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds email notification channel support using SMTP to the Backend.AI notification system, enabling notifications to be sent via email in addition to the existing webhook channel.
Changes:
- Introduces
EmailConfig,SMTPConfig, andEmailMessageConfigdata models for email channel configuration - Implements
EmailChannelwith SMTP support for both authenticated and relay servers - Updates the notification system to handle multiple channel types through a union type pattern
- Adds comprehensive test coverage for email functionality and configuration validation
Reviewed changes
Copilot reviewed 27 out of 33 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/ai/backend/common/data/notification/types.py | Adds base NotificationChannelConfig class and email-related config models (EmailConfig, SMTPConfig, EmailMessageConfig); introduces NotificationChannelConfigTypes union |
| src/ai/backend/common/data/notification/init.py | Exports new email configuration types |
| src/ai/backend/common/dto/manager/notification/request.py | Updates request models to use NotificationChannelConfigTypes union instead of webhook-only config |
| src/ai/backend/common/dto/manager/notification/response.py | Adds ConfigResponse base class and EmailConfigResponse for API responses |
| src/ai/backend/common/exception.py | Adds NotificationChannelBadRequest and NotificationChannelNotSupported exceptions |
| src/ai/backend/manager/data/notification/types.py | Updates NotificationChannelData to use generic NotificationChannelConfig |
| src/ai/backend/manager/models/notification/row.py | Adds email config parsing in to_data() method |
| src/ai/backend/manager/repositories/notification/creators.py | Updates creator spec to use NotificationChannelConfig |
| src/ai/backend/manager/repositories/notification/updaters.py | Updates updater spec and adds from_request() factory method |
| src/ai/backend/manager/notification/notification_center.py | Adds EMAIL channel type handling with type validation |
| src/ai/backend/manager/notification/channels/email/channel.py | Implements EmailChannel class with async SMTP sending |
| src/ai/backend/manager/api/notification/adapter.py | Updates adapter to handle multiple config types in responses and simplifies updater building |
| src/ai/backend/manager/api/gql/notification/types.py | Adds GraphQL types for email config and implements OneOf pattern for channel config input |
| src/ai/backend/client/cli/notification.py | Updates CLI to support email channel creation/updates with JSON config |
| tests/unit/common/data/notification/test_notification_config_schema.py | Tests union type validation and conversion behavior |
| tests/unit/manager/notification/channels/email/test_channel.py | Comprehensive unit tests for EmailChannel functionality |
| tests/unit/manager/api/notification/test_adapter.py | Adds type assertion for webhook config |
| tests/unit/manager/repositories/notification/test_notification_repository.py | Adds config type assertions |
| docs/manager/graphql-reference/v2-schema.graphql | Updates GraphQL schema with email types and OneOf directive |
| docs/manager/graphql-reference/supergraph.graphql | Updates federated schema |
| changes/7941.feature.md | Documents the new feature |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
9b0a5df to
d1cf631
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 27 out of 33 changed files in this pull request and generated 9 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
7231afc to
36ac16d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 27 out of 33 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
tests/unit/manager/repositories/notification/test_notification_repository.py:408
- Missing test coverage for email channel configuration in repository tests. While webhook channels are tested in test_create_channel and test_update_channel, there are no equivalent tests for EmailConfig. The repository should be tested with both channel types to ensure the config serialization/deserialization and type handling work correctly for email channels. Consider adding test cases similar to test_create_channel but using EmailConfig to ensure the model's to_data method correctly parses email configurations from the database.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
e4c7059 to
7f3e74c
Compare
fregataa
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to add WebhookNotificationChannelData and EmailNotificationChannelData types and functions which convert NotificationChannelData to them
HyeockJinKim
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this work only applies to CLI / API / GraphQL, that's fine, but including Email Channel support in the same PR is excessive. Please split the PR and issues:
- Email Channel implementation
- Registration work for API / GraphQL / CLI (or these could potentially be combined since they're mostly boilerplate)
f796450 to
6fea80e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 29 out of 33 changed files in this pull request and generated 15 comments.
Comments suppressed due to low confidence (4)
src/ai/backend/client/cli/notification.py:139
- The CLI create_channel_cmd only supports webhook configuration through the URL argument. With email channel support being added, the CLI should be updated to support email channel creation as well, possibly by accepting different configuration parameters based on the channel_type or through a configuration file.
@channel.command("create")
@pass_ctx_obj
@click.argument("name", type=str)
@click.argument("url", type=str)
@click.option("--channel-type", default="webhook", help="Channel type (default: WEBHOOK)")
@click.option("--description", type=str, default=None, help="Channel description")
@click.option("--disabled", is_flag=True, help="Create channel as disabled")
def create_channel_cmd(
ctx: CLIContext,
name: str,
url: str,
channel_type: str,
description: Optional[str],
disabled: bool,
) -> None:
"""
Create a new notification channel.
\b
NAME: Channel name
URL: Webhook URL
"""
with Session() as session:
try:
request = CreateNotificationChannelRequest(
name=name,
channel_type=NotificationChannelType(channel_type),
config=WebhookSpec(url=url),
description=description,
enabled=not disabled,
)
result = session.Notification.create_channel(request)
channel = result.channel
print_done(f"Channel created: {channel.id}")
print(json.dumps(channel.model_dump(mode="json"), indent=2, default=str))
except Exception as e:
ctx.output.print_error(e)
sys.exit(ExitCode.FAILURE)
src/ai/backend/manager/api/notification/adapter.py:114
- The build_updater method only supports WebhookSpec configuration updates. To support updating email channels through the REST API, this method should handle both WebhookSpec and EmailSpec types.
def build_updater(
self, request: UpdateNotificationChannelRequest, channel_id: UUID
) -> Updater[NotificationChannelRow]:
"""Convert update request to updater."""
name = OptionalState[str].nop()
description = OptionalState[str | None].nop()
config = OptionalState[WebhookSpec].nop()
enabled = OptionalState[bool].nop()
if request.name is not None:
name = OptionalState.update(request.name)
if request.description is not None:
description = OptionalState.update(request.description)
if request.config is not None:
# config validator ensures this is WebhookSpec
if not isinstance(request.config, WebhookSpec):
raise InvalidNotificationConfig(
f"Expected WebhookSpec, got {type(request.config).__name__}"
)
config = OptionalState.update(request.config)
if request.enabled is not None:
enabled = OptionalState.update(request.enabled)
spec = NotificationChannelUpdaterSpec(
name=name,
description=description,
config=config,
enabled=enabled,
)
return Updater(spec=spec, pk_value=channel_id)
src/ai/backend/manager/api/notification/adapter.py:82
- The REST API adapter only supports WebhookSpec in convert_to_dto and will raise an InvalidNotificationConfig error for email channels. This prevents listing or retrieving email channels through the REST API. The method should handle both WebhookSpec and EmailSpec, creating appropriate response DTOs for each type.
def convert_to_dto(self, data: NotificationChannelData) -> NotificationChannelDTO:
"""Convert NotificationChannelData to DTO."""
# TODO: Support EmailSpec when Email channel is implemented
if not isinstance(data.config, WebhookSpec):
raise InvalidNotificationConfig(
f"Expected WebhookSpec, got {type(data.config).__name__}"
)
return NotificationChannelDTO(
id=data.id,
name=data.name,
description=data.description,
channel_type=data.channel_type,
config=WebhookSpecResponse(url=data.config.url),
enabled=data.enabled,
created_at=data.created_at,
created_by=data.created_by,
updated_at=data.updated_at,
)
src/ai/backend/manager/api/gql/notification/types.py:70
- The to_internal method for NotificationChannelTypeGQL does not handle all possible enum values. It only has a case for WEBHOOK, but if EMAIL is added to the enum (as it should be), this method will fail with an implicit None return. Add a case for EMAIL that returns NotificationChannelType.EMAIL.
def to_internal(self) -> NotificationChannelType:
"""Convert GraphQL enum to internal NotificationChannelType."""
match self:
case NotificationChannelTypeGQL.WEBHOOK:
return NotificationChannelType.WEBHOOK
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
tests/unit/manager/services/notification/test_notification_service.py
Outdated
Show resolved
Hide resolved
src/ai/backend/manager/notification/channels/webhook/channel.py
Outdated
Show resolved
Hide resolved
cfc0a4a to
f6cc84f
Compare
Co-authored-by: octodog <[email protected]>
| description: Optional[str] = None | ||
| channel_type: NotificationChannelTypeGQL = NotificationChannelTypeGQL.WEBHOOK | ||
| config: WebhookConfigInput = strawberry.field() | ||
| config: WebhookSpecInput = strawberry.field() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to accept emailConfig in addition to WebHook.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will added it in follow up PR (REST, GQL, CLI support)
| description: Optional[str] = None | ||
| channel_type: NotificationChannelTypeGQL = NotificationChannelTypeGQL.WEBHOOK | ||
| config: WebhookConfigInput = strawberry.field() | ||
| config: WebhookSpecInput = strawberry.field() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it a future plan to add a new EmailSpecInput?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I will add it in follow up issue
54f3fe4 to
458cdb0
Compare
4f7f9da to
49b7113
Compare
Co-authored-by: octodog <[email protected]>
resolves #7692 (BA-3635)
Checklist: (if applicable)
ai.backend.testdocsdirectory📚 Documentation preview 📚: https://sorna--7941.org.readthedocs.build/en/7941/
📚 Documentation preview 📚: https://sorna-ko--7941.org.readthedocs.build/ko/7941/