From aab67ef37835048a9f3f4eef4cb786fa159efafd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 17:01:40 +0000 Subject: [PATCH 01/10] chore(pip-prod)(deps): bump pyparsing from 3.1.1 to 3.1.2 Bumps [pyparsing](https://github.com/pyparsing/pyparsing) from 3.1.1 to 3.1.2. - [Release notes](https://github.com/pyparsing/pyparsing/releases) - [Changelog](https://github.com/pyparsing/pyparsing/blob/master/CHANGES) - [Commits](https://github.com/pyparsing/pyparsing/compare/3.1.1...pyparsing_3.1.2) --- updated-dependencies: - dependency-name: pyparsing dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0e04502..dafdd56 100644 --- a/requirements.txt +++ b/requirements.txt @@ -27,7 +27,7 @@ pluggy==1.4.0 py==1.11.0 pycodestyle==2.11.1 PyJWT==2.8.0 -pyparsing==3.1.1 +pyparsing==3.1.2 python-dateutil==2.9.0.post0 python-dotenv==1.0.1 PyYAML==6.0.1 -- 2.45.2 From 37ae25adbee907de967d81524320f7f6719b4ee2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:38:31 +0000 Subject: [PATCH 02/10] chore(pip-prod)(deps): bump packaging from 23.2 to 24.0 Bumps [packaging](https://github.com/pypa/packaging) from 23.2 to 24.0. - [Release notes](https://github.com/pypa/packaging/releases) - [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pypa/packaging/compare/23.2...24.0) --- updated-dependencies: - dependency-name: packaging dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index dafdd56..c2f0fb0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,7 +20,7 @@ lazy-object-proxy==1.10.0 mccabe==0.7.0 mypy-extensions==1.0.0 nodeenv==1.8.0 -packaging==23.2 +packaging==24.0 pathspec==0.12.1 platformdirs==4.2.0 pluggy==1.4.0 -- 2.45.2 From 26b846d297b6fb77d5c5cef5c958fcf40a73a397 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Mar 2024 07:50:05 +0000 Subject: [PATCH 03/10] chore(actions)(deps): bump mathieudutour/github-tag-action Bumps [mathieudutour/github-tag-action](https://github.com/mathieudutour/github-tag-action) from 6.1 to 6.2. - [Release notes](https://github.com/mathieudutour/github-tag-action/releases) - [Commits](https://github.com/mathieudutour/github-tag-action/compare/v6.1...v6.2) --- updated-dependencies: - dependency-name: mathieudutour/github-tag-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5d54b68..13ea1f3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v4 - name: Bump version and push tag id: tag_version - uses: mathieudutour/github-tag-action@v6.1 + uses: mathieudutour/github-tag-action@v6.2 with: github_token: ${{ secrets.GITHUB_TOKEN }} default_bump: minor -- 2.45.2 From 041299d3fc5805026f426628ee3ff96be8821cc2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 16:51:33 +0000 Subject: [PATCH 04/10] chore(pip-prod)(deps): bump autopep8 from 2.0.4 to 2.1.0 Bumps [autopep8](https://github.com/hhatto/autopep8) from 2.0.4 to 2.1.0. - [Release notes](https://github.com/hhatto/autopep8/releases) - [Commits](https://github.com/hhatto/autopep8/compare/v2.0.4...v2.1.0) --- updated-dependencies: - dependency-name: autopep8 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c2f0fb0..71cced3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ appdirs==1.4.4 astroid==3.1.0 attrs==23.2.0 -autopep8==2.0.4 +autopep8==2.1.0 backoff==2.2.1 certifi==2024.2.2 cfgv==3.4.0 -- 2.45.2 From 4687a6aa38d8885567e1ebec767d27856338ffdd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 16:57:04 +0000 Subject: [PATCH 05/10] chore(pip-prod)(deps): bump filelock from 3.13.1 to 3.13.2 Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.13.1 to 3.13.2. - [Release notes](https://github.com/tox-dev/py-filelock/releases) - [Changelog](https://github.com/tox-dev/filelock/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/py-filelock/compare/3.13.1...3.13.2) --- updated-dependencies: - dependency-name: filelock dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 71cced3..3fc7a99 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ click==8.1.7 coloredlogs==15.0.1 dill==0.3.8 distlib==0.3.8 -filelock==3.13.1 +filelock==3.13.2 future==1.0.0 humanfriendly==10.0 identify==2.5.35 -- 2.45.2 From 1de66ee3c66842d1a805cb3f5f1cd3d3071d593a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Mar 2024 16:23:38 +0000 Subject: [PATCH 06/10] chore(pip-prod)(deps): bump filelock from 3.13.2 to 3.13.3 Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.13.2 to 3.13.3. - [Release notes](https://github.com/tox-dev/py-filelock/releases) - [Changelog](https://github.com/tox-dev/filelock/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/py-filelock/compare/3.13.2...3.13.3) --- updated-dependencies: - dependency-name: filelock dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3fc7a99..59a8580 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ click==8.1.7 coloredlogs==15.0.1 dill==0.3.8 distlib==0.3.8 -filelock==3.13.2 +filelock==3.13.3 future==1.0.0 humanfriendly==10.0 identify==2.5.35 -- 2.45.2 From dc62af1574c2b70baab81379a57c4ce8bbe217d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:18:48 +0000 Subject: [PATCH 07/10] chore(pip-prod)(deps): bump filelock from 3.13.3 to 3.13.4 Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.13.3 to 3.13.4. - [Release notes](https://github.com/tox-dev/py-filelock/releases) - [Changelog](https://github.com/tox-dev/filelock/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/py-filelock/compare/3.13.3...3.13.4) --- updated-dependencies: - dependency-name: filelock dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 59a8580..1be84fc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ click==8.1.7 coloredlogs==15.0.1 dill==0.3.8 distlib==0.3.8 -filelock==3.13.3 +filelock==3.13.4 future==1.0.0 humanfriendly==10.0 identify==2.5.35 -- 2.45.2 From 6300b11591193c5918cf46535278f36c7bf043a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Apr 2024 17:00:18 +0000 Subject: [PATCH 08/10] chore(pip-prod)(deps): bump idna from 3.6 to 3.7 Bumps [idna](https://github.com/kjd/idna) from 3.6 to 3.7. - [Release notes](https://github.com/kjd/idna/releases) - [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.rst) - [Commits](https://github.com/kjd/idna/compare/v3.6...v3.7) --- updated-dependencies: - dependency-name: idna dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1be84fc..ba9df2b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ filelock==3.13.4 future==1.0.0 humanfriendly==10.0 identify==2.5.35 -idna==3.6 +idna==3.7 iniconfig==2.0.0 lazy-object-proxy==1.10.0 mccabe==0.7.0 -- 2.45.2 From 97be51bd59c335557ab071e1c0deac59db4f55a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Apr 2024 17:00:14 +0000 Subject: [PATCH 09/10] chore(pip-prod)(deps): bump virtualenv from 20.25.1 to 20.25.3 Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.25.1 to 20.25.3. - [Release notes](https://github.com/pypa/virtualenv/releases) - [Changelog](https://github.com/pypa/virtualenv/blob/main/docs/changelog.rst) - [Commits](https://github.com/pypa/virtualenv/compare/20.25.1...20.25.3) --- updated-dependencies: - dependency-name: virtualenv dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ba9df2b..ba6e896 100644 --- a/requirements.txt +++ b/requirements.txt @@ -38,7 +38,7 @@ toml==0.10.2 tomli==2.0.1 tomlkit==0.12.4 urllib3==2.2.1 -virtualenv==20.25.1 +virtualenv==20.25.3 webex-bot==0.4.1 webexteamssdk==1.6.1 websockets==10.2 -- 2.45.2 From 123ed8aa15705cceb635e9923a9ecd43aa104124 Mon Sep 17 00:00:00 2001 From: Luke Tainton Date: Sun, 21 Apr 2024 16:17:00 +0100 Subject: [PATCH 10/10] Add APM (#184) * Add APM * Fix unit tests * Remove apm.py * Add SonarCloud recommendations * SonarCloud python:S6890 --- .env.default | 3 ++ app/commands/submit_task.py | 17 ++++++---- app/main.py | 15 ++++++++- app/utils/config.py | 33 ++++++++++++++++++- app/utils/datetime.py | 13 +++++++- app/utils/n8n.py | 64 +++++++++++++++++++++++++++---------- requirements.txt | 1 + test.sh | 2 +- tests/__init__.py | 0 tests/test_config.py | 2 ++ 10 files changed, 124 insertions(+), 26 deletions(-) create mode 100644 tests/__init__.py diff --git a/.env.default b/.env.default index 2991382..f28208d 100644 --- a/.env.default +++ b/.env.default @@ -1,3 +1,6 @@ +APP_LIFECYCLE="dev" +SENTRY_ENABLED="False" +SENTRY_DSN="" ADMIN_EMAIL="" ADMIN_FIRST_NAME="" BOT_NAME="" diff --git a/app/commands/submit_task.py b/app/commands/submit_task.py index 6a19ff0..70ef360 100644 --- a/app/commands/submit_task.py +++ b/app/commands/submit_task.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import logging +import sentry_sdk from webex_bot.models.command import Command from webex_bot.models.response import Response, response_from_adaptive_card @@ -113,7 +114,8 @@ class SubmitTaskCommand(Command): Submit(title="Cancel", data={"command_keyword": "exit"}), ], ) - return response_from_adaptive_card(card) + with sentry_sdk.start_transaction(name="submit_task_command"): + return response_from_adaptive_card(card) class SubmitTaskCallback(Command): @@ -147,7 +149,8 @@ class SubmitTaskCallback(Command): ) def execute(self, message, attachment_actions, activity) -> str: - return self.msg + with sentry_sdk.start_transaction(name="submit_task_callback"): + return self.msg class MyTasksCallback(Command): @@ -158,11 +161,13 @@ class MyTasksCallback(Command): self.msg: str = "" def pre_execute(self, message, attachment_actions, activity) -> str: - return "Getting your tasks..." + with sentry_sdk.start_transaction(name="my_tasks_preexec"): + return "Getting your tasks..." def execute(self, message, attachment_actions, activity) -> str | None: sender: str = attachment_actions.inputs.get("sender") result: bool = get_tasks(requestor=sender) - if not result: - return "Failed to get tasks. Please try again." - return + with sentry_sdk.start_transaction(name="my_tasks_exec"): + if not result: + return "Failed to get tasks. Please try again." + return diff --git a/app/main.py b/app/main.py index b84f46a..4affc14 100644 --- a/app/main.py +++ b/app/main.py @@ -1,5 +1,8 @@ #!/usr/bin/env python3 +import sentry_sdk +from sentry_sdk.integrations.stdlib import StdlibIntegration + from webex_bot.webex_bot import WebexBot from app.commands.exit import ExitCommand @@ -7,8 +10,18 @@ from app.commands.submit_task import SubmitTaskCommand from app.utils.config import config +if config.sentry_enabled: + apm = sentry_sdk.init( + dsn=config.sentry_dsn, + enable_tracing=True, + environment=config.environment, + integrations=[StdlibIntegration()], + spotlight=True + ) + + def create_bot() -> WebexBot: - # Create a Bot Object + """Create and return a Webex Bot object.""" webex_bot: WebexBot = WebexBot( bot_name=config.bot_name, teams_bot_token=config.webex_token, diff --git a/app/utils/config.py b/app/utils/config.py index 27f6e32..f12eb7a 100644 --- a/app/utils/config.py +++ b/app/utils/config.py @@ -1,34 +1,65 @@ -#!/usr/bin/env python3 +"""Configuration module.""" import os class Config: + """Configuration module.""" def __init__(self) -> None: + """Configuration module.""" + self.__environment: str = os.environ.get("APP_LIFECYCLE", "DEV").upper() self.__bot_name: str = os.environ["BOT_NAME"] self.__webex_token: str = os.environ["WEBEX_API_KEY"] self.__admin_first_name: str = os.environ["ADMIN_FIRST_NAME"] self.__admin_emails: list = os.environ["ADMIN_EMAIL"].split(",") self.__n8n_webhook_url: str = os.environ["N8N_WEBHOOK_URL"] + self.__sentry_dsn: str = os.environ.get("SENTRY_DSN", "") + self.__sentry_enabled: bool = bool( + os.environ.get("SENTRY_ENABLED", "False").upper() == "TRUE" + and self.__sentry_dsn != "" + ) + + @property + def environment(self) -> str: + """Returns the current app lifecycle.""" + return self.__environment + + @property + def sentry_enabled(self) -> bool: + """Returns True if Sentry SDK is enabled, else False.""" + return self.__sentry_enabled + + @property + def sentry_dsn(self) -> str: + """Returns the Sentry DSN value if Sentry SDK is enabled AND + Sentry DSN is set, else blank string.""" + if not self.__sentry_enabled: + return "" + return self.__sentry_dsn @property def bot_name(self) -> str: + """Returns the bot name.""" return self.__bot_name @property def webex_token(self) -> str: + """Returns the Webex API key.""" return self.__webex_token @property def admin_first_name(self) -> str: + """Returns the first name of the bot admin.""" return self.__admin_first_name @property def admin_emails(self) -> list: + """Returns a list of admin email addresses.""" return self.__admin_emails @property def n8n_webhook_url(self) -> str: + """Returns the n8n webhook URL.""" return self.__n8n_webhook_url diff --git a/app/utils/datetime.py b/app/utils/datetime.py index 8526bb1..472371c 100644 --- a/app/utils/datetime.py +++ b/app/utils/datetime.py @@ -1,7 +1,18 @@ #!/usr/bin/env python3 +from zoneinfo import ZoneInfo + from datetime import datetime def timestamp_to_date(timestamp: int) -> str: - return datetime.utcfromtimestamp(timestamp).strftime("%Y-%m-%d") + """Convert timestamp to date. + + Args: + timestamp (int): Timestamp to convert. + + Returns: + str: Date in the format YYYY-MM-DD. + """ + return datetime.fromtimestamp(timestamp=timestamp, tz=ZoneInfo("UTC")).strftime("%Y-%m-%d") + diff --git a/app/utils/n8n.py b/app/utils/n8n.py index e1d8e31..4c0f84b 100644 --- a/app/utils/n8n.py +++ b/app/utils/n8n.py @@ -1,9 +1,18 @@ import requests +import sentry_sdk from app.utils.config import config def __n8n_post(data: dict) -> bool: + """Post data to N8N webhook URL. + + Args: + data (dict): Data to post to webhook URL. + + Returns: + bool: True if successful, else False. + """ headers: dict = {"Content-Type": "application/json"} resp: requests.Response = requests.post( url=config.n8n_webhook_url, @@ -16,22 +25,45 @@ def __n8n_post(data: dict) -> bool: def submit_task(summary, description, completion_date, requestor) -> bool: - data: dict = { - "requestor": requestor, - "title": summary, - "description": description, - "completiondate": completion_date, - } - return __n8n_post(data=data) + """Submit task to N8N webhook URL. + + Args: + summary (str): Summary of task. + description (str): Description of task. + completion_date (str): Completion date of task. + requestor (str): Requestor of task. + + Returns: + bool: True if successful, else False. + """ + with sentry_sdk.start_transaction(name="submit_task"): + data: dict = { + "requestor": requestor, + "title": summary, + "description": description, + "completiondate": completion_date, + } + _data = __n8n_post(data=data) + return _data def get_tasks(requestor) -> bool: - headers: dict = {"Content-Type": "application/json"} - resp: requests.Response = requests.get( - url=config.n8n_webhook_url, - headers=headers, - timeout=10, - verify=False, - params={"requestor": requestor}, - ) - return bool(resp.status_code == 200) + """Get tasks from N8N webhook URL. + + Args: + requestor (str): Requestor of tasks. + + Returns: + bool: True if successful, else False. + """ + with sentry_sdk.start_transaction(name="get_tasks"): + headers: dict = {"Content-Type": "application/json"} + resp: requests.Response = requests.get( + url=config.n8n_webhook_url, + headers=headers, + timeout=10, + verify=False, + params={"requestor": requestor}, + ) + _data = bool(resp.status_code == 200) + return _data diff --git a/requirements.txt b/requirements.txt index ba6e896..f1aee33 100644 --- a/requirements.txt +++ b/requirements.txt @@ -33,6 +33,7 @@ python-dotenv==1.0.1 PyYAML==6.0.1 requests==2.31.0 requests-toolbelt==1.0.0 +sentry-sdk==1.45.0 six==1.16.0 toml==0.10.2 tomli==2.0.1 diff --git a/test.sh b/test.sh index b0f95fc..1fcaf08 100755 --- a/test.sh +++ b/test.sh @@ -1,3 +1,3 @@ export $(cat .env | xargs) python -B -m app.main -unset BOT_NAME WEBEX_API_KEY ADMIN_FIRST_NAME ADMIN_EMAIL N8N_WEBHOOK_URL \ No newline at end of file +unset APP_LIFECYCLE SENTRY_ENABLED SENTRY_DSN BOT_NAME WEBEX_API_KEY ADMIN_FIRST_NAME ADMIN_EMAIL N8N_WEBHOOK_URL diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_config.py b/tests/test_config.py index ab41d77..ebebd1c 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -11,6 +11,8 @@ vars: dict = { "ADMIN_FIRST_NAME": "Test", "ADMIN_EMAIL": "test@test.com", "N8N_WEBHOOK_URL": "https://n8n.test.com/webhook/abcdefg", + "SENTRY_ENABLED": "false", + "SENTRY_DSN": "http://localhost" } -- 2.45.2