Unverified Commit 97678d63 authored by Tamika Tannis's avatar Tamika Tannis Committed by GitHub

Custom routes + Further notification cleanup (#309)

* Further notification cleanup

* Catch any errors getting the user for metrics + add doc for INIT_CUSTOM_ROUTES

* Revert change to _build_metrics
parent 9239d1cf
...@@ -56,4 +56,8 @@ def create_app(config_module_class: str, template_folder: str = None) -> Flask: ...@@ -56,4 +56,8 @@ def create_app(config_module_class: str, template_folder: str = None) -> Flask:
app.register_blueprint(search_blueprint) app.register_blueprint(search_blueprint)
init_routes(app) init_routes(app)
init_custom_routes = app.config.get('INIT_CUSTOM_ROUTES')
if init_custom_routes:
init_custom_routes(app)
return app return app
import logging import logging
from http import HTTPStatus from http import HTTPStatus
from enum import Enum
from flask import current_app as app from flask import current_app as app
from flask import jsonify, make_response, Response from flask import jsonify, make_response, Response
...@@ -9,8 +10,27 @@ from typing import Dict, List ...@@ -9,8 +10,27 @@ from typing import Dict, List
from amundsen_application.api.exceptions import MailClientNotImplemented from amundsen_application.api.exceptions import MailClientNotImplemented
from amundsen_application.log.action_log import action_logging from amundsen_application.log.action_log import action_logging
class NotificationType(str, Enum):
"""
Enum to describe supported notification types. Must match NotificationType interface defined in:
https://github.com/lyft/amundsenfrontendlibrary/blob/master/amundsen_application/static/js/interfaces/Notifications.ts
"""
OWNER_ADDED = 'owner_added'
OWNER_REMOVED = 'owner_removed'
METADATA_EDITED = 'metadata_edited'
METADATA_REQUESTED = 'metadata_requested'
@classmethod
def has_value(cls, value: str) -> bool:
for key in cls:
if key.value == value:
return True
return False
NOTIFICATION_STRINGS = { NOTIFICATION_STRINGS = {
'added': { NotificationType.OWNER_ADDED.value: {
'comment': ('<br/>What is expected of you?<br/>As an owner, you take an important part in making ' 'comment': ('<br/>What is expected of you?<br/>As an owner, you take an important part in making '
'sure that the datasets you own can be used as swiftly as possible across the company.<br/>' 'sure that the datasets you own can be used as swiftly as possible across the company.<br/>'
'Make sure the metadata is correct and up to date.<br/>'), 'Make sure the metadata is correct and up to date.<br/>'),
...@@ -20,14 +40,14 @@ NOTIFICATION_STRINGS = { ...@@ -20,14 +40,14 @@ NOTIFICATION_STRINGS = {
'notification': ('<br/>You have been added to the owners list of the <a href="{resource_url}">' 'notification': ('<br/>You have been added to the owners list of the <a href="{resource_url}">'
'{resource_name}</a> dataset by {sender}.<br/>'), '{resource_name}</a> dataset by {sender}.<br/>'),
}, },
'removed': { NotificationType.OWNER_REMOVED.value: {
'comment': '', 'comment': '',
'end_note': ('<br/>If you think you have been incorrectly removed as an owner, ' 'end_note': ('<br/>If you think you have been incorrectly removed as an owner, '
'add yourself back to the owners list.<br/>'), 'add yourself back to the owners list.<br/>'),
'notification': ('<br/>You have been removed from the owners list of the <a href="{resource_url}">' 'notification': ('<br/>You have been removed from the owners list of the <a href="{resource_url}">'
'{resource_name}</a> dataset by {sender}.<br/>'), '{resource_name}</a> dataset by {sender}.<br/>'),
}, },
'requested': { NotificationType.METADATA_REQUESTED.value: {
'comment': '', 'comment': '',
'end_note': '<br/>Please visit the provided link and improve descriptions on that resource.<br/>', 'end_note': '<br/>Please visit the provided link and improve descriptions on that resource.<br/>',
'notification': '<br/>{sender} is trying to use <a href="{resource_url}">{resource_name}</a>, ', 'notification': '<br/>{sender} is trying to use <a href="{resource_url}">{resource_name}</a>, ',
...@@ -84,7 +104,7 @@ def get_notification_html(*, notification_type: str, options: Dict, sender: str) ...@@ -84,7 +104,7 @@ def get_notification_html(*, notification_type: str, options: Dict, sender: str)
end_note = notification_strings.get('end_note', '') end_note = notification_strings.get('end_note', '')
salutation = '<br/>Thanks,<br/>Amundsen Team' salutation = '<br/>Thanks,<br/>Amundsen Team'
if notification_type == 'requested': if notification_type == NotificationType.METADATA_REQUESTED:
options_comment = options.get('comment') options_comment = options.get('comment')
need_resource_description = options.get('description_requested') need_resource_description = options.get('description_requested')
need_fields_descriptions = options.get('fields_requested') need_fields_descriptions = options.get('fields_requested')
...@@ -118,12 +138,15 @@ def get_notification_subject(*, notification_type: str, options: Dict) -> str: ...@@ -118,12 +138,15 @@ def get_notification_subject(*, notification_type: str, options: Dict) -> str:
""" """
resource_name = options.get('resource_name') resource_name = options.get('resource_name')
notification_subject_dict = { notification_subject_dict = {
'added': 'You are now an owner of {}'.format(resource_name), NotificationType.OWNER_ADDED.value: 'You are now an owner of {}'.format(resource_name),
'removed': 'You have been removed as an owner of {}'.format(resource_name), NotificationType.OWNER_REMOVED.value: 'You have been removed as an owner of {}'.format(resource_name),
'edited': 'Your dataset {}\'s metadata has been edited'.format(resource_name), NotificationType.METADATA_EDITED.value: 'Your dataset {}\'s metadata has been edited'.format(resource_name),
'requested': 'Request for metadata on {}'.format(resource_name), NotificationType.METADATA_REQUESTED.value: 'Request for metadata on {}'.format(resource_name),
} }
return notification_subject_dict.get(notification_type, '') subject = notification_subject_dict.get(notification_type)
if subject is None:
raise Exception('Unsupported notification_type')
return subject
def send_notification(*, notification_type: str, options: Dict, recipients: List, sender: str) -> Response: def send_notification(*, notification_type: str, options: Dict, recipients: List, sender: str) -> Response:
...@@ -174,7 +197,7 @@ def send_notification(*, notification_type: str, options: Dict, recipients: List ...@@ -174,7 +197,7 @@ def send_notification(*, notification_type: str, options: Dict, recipients: List
subject=subject, subject=subject,
html=html, html=html,
optional_data={ optional_data={
'email_type': 'notification' 'email_type': notification_type,
}, },
) )
status_code = response.status_code status_code = response.status_code
......
import os import os
from typing import Dict, Optional, Set # noqa: F401 from typing import Callable, Dict, Optional, Set # noqa: F401
from flask import Flask # noqa: F401
from amundsen_application.tests.test_utils import get_test_user from amundsen_application.tests.test_utils import get_test_user
...@@ -22,6 +25,9 @@ class Config: ...@@ -22,6 +25,9 @@ class Config:
MAIL_CLIENT = None MAIL_CLIENT = None
NOTIFICATIONS_ENABLED = False NOTIFICATIONS_ENABLED = False
# Initialize custom routes
INIT_CUSTOM_ROUTES = None # type: Callable[[Flask], None]
class LocalConfig(Config): class LocalConfig(Config):
DEBUG = False DEBUG = False
......
// TODO: Remove notification types that can be triggered in flask layer if necessary
export enum NotificationType { export enum NotificationType {
OWNER_ADDED = 'added', OWNER_ADDED = 'owner_added',
OWNER_REMOVED = 'removed', OWNER_REMOVED = 'owner_removed',
METADATA_EDITED = 'edited', METADATA_EDITED = 'metadata_edited',
METADATA_REQUESTED = 'requested', METADATA_REQUESTED = 'metadata_requested',
} }
export enum RequestMetadataType { export enum RequestMetadataType {
......
...@@ -4,6 +4,18 @@ After modifying any variable in [config.py](https://github.com/lyft/amundsenfron ...@@ -4,6 +4,18 @@ After modifying any variable in [config.py](https://github.com/lyft/amundsenfron
**NOTE: This document is a work in progress and does not include 100% of features. We welcome PRs to complete this document** **NOTE: This document is a work in progress and does not include 100% of features. We welcome PRs to complete this document**
## Custom Routes
In order to add any custom Flask endpoints to Amundsen's frontend application, configure a function on the `INIT_CUSTOM_ROUTES` variable. This function takes the created Flask application and can leverage Flask's [add_url_rule](https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.add_url_rule) method to add custom routes.
Example: Setting `INIT_CUSTOM_ROUTES` to the `init_custom_routes` method below will expose a `/custom_route` endpoint on the frontend application.
```bash
def init_custom_routes(app: Flask) -> None:
app.add_url_rule('/custom_route', 'custom_route', custom_route)
def custom_route():
pass
```
## Mail Client Features ## Mail Client Features
Amundsen has two features that leverage the custom mail client -- the feedback tool and notifications. For these features a custom implementation of [base_mail_client](https://github.com/lyft/amundsenfrontendlibrary/blob/master/amundsen_application/base/base_mail_client.py) must be mapped to the `MAIL_CLIENT` configuration variable. Amundsen has two features that leverage the custom mail client -- the feedback tool and notifications. For these features a custom implementation of [base_mail_client](https://github.com/lyft/amundsenfrontendlibrary/blob/master/amundsen_application/base/base_mail_client.py) must be mapped to the `MAIL_CLIENT` configuration variable.
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment