From aed9399e0c3c1ef4259d3df62511f677c54b0ecb Mon Sep 17 00:00:00 2001 From: itisallgood <25401000+itisallgood@users.noreply.github.com> Date: Wed, 3 Jul 2024 16:47:24 +0200 Subject: [PATCH 1/8] * Added name and namespace variables for K8sJobFailure playbook's title (#1486) --- helm/robusta/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm/robusta/values.yaml b/helm/robusta/values.yaml index bf0135e39..253a3890d 100644 --- a/helm/robusta/values.yaml +++ b/helm/robusta/values.yaml @@ -526,7 +526,7 @@ platformPlaybooks: actions: - create_finding: aggregation_key: "JobFailure" - title: "Job Failed" + title: "Job $name on namespace $namespace failed" - job_info_enricher: {} - job_events_enricher: {} - job_pod_enricher: {} From 0b63f9dcb8440543f05235cba691dda453de0432 Mon Sep 17 00:00:00 2001 From: arik Date: Wed, 3 Jul 2024 17:48:05 +0300 Subject: [PATCH 2/8] bug when loading custom certificate too late - some http clients were initialized before it was loaded, caused ssl errors (#1485) --- .flake8 | 2 +- src/robusta/runner/main.py | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.flake8 b/.flake8 index 60991418b..197504dd9 100644 --- a/.flake8 +++ b/.flake8 @@ -12,4 +12,4 @@ exclude = .git, .mypy_cache, src/robusta/integrations/kubernetes/autogenerated, src/robusta/integrations/kubernetes/custom_models.py -ignore = E501, W503, E203 +ignore = E501, W503, E203, E402 diff --git a/src/robusta/runner/main.py b/src/robusta/runner/main.py index 674d94ccf..6c1933ac0 100644 --- a/src/robusta/runner/main.py +++ b/src/robusta/runner/main.py @@ -1,7 +1,18 @@ +import os + +from robusta.runner.ssl_utils import add_custom_certificate + +ADDITIONAL_CERTIFICATE: str = os.environ.get("CERTIFICATE", "") + +if add_custom_certificate(ADDITIONAL_CERTIFICATE): + print("added custom certificate") + +# DO NOT ADD ANY CODE ABOVE THIS +# ADDING IMPORTS BEFORE ADDING THE CUSTOM CERTS MIGHT INIT HTTP CLIENTS THAT DOESN'T RESPECT THE CUSTOM CERT + import signal from robusta.core.model.env_vars import ( - ADDITIONAL_CERTIFICATE, ENABLE_TELEMETRY, ROBUSTA_TELEMETRY_ENDPOINT, SEND_ADDITIONAL_TELEMETRY, @@ -13,7 +24,6 @@ from robusta.runner.config_loader import ConfigLoader from robusta.runner.log_init import init_logging, logging from robusta.runner.process_setup import process_setup -from robusta.runner.ssl_utils import add_custom_certificate from robusta.runner.telemetry_service import TelemetryLevel, TelemetryService from robusta.runner.web import Web from robusta.utils.server_start import ServerStart @@ -23,8 +33,6 @@ def main(): process_setup() init_logging() ServerStart.set() - if add_custom_certificate(ADDITIONAL_CERTIFICATE): - logging.info("added custom certificate") create_monkey_patches() registry = Registry() From 5a960e19164af40dc79519a2a08de54f548b9185 Mon Sep 17 00:00:00 2001 From: itisallgood <25401000+itisallgood@users.noreply.github.com> Date: Sun, 7 Jul 2024 13:50:48 +0200 Subject: [PATCH 3/8] Added support for webhook_url dynamic overriding using annotations (issue 1083) (#1476) * Created MsTeamsWebhookUrlTransformer to enable overriding the webhook_url using annotations from workload or from environment variable --- docs/configuration/sinks/ms-teams.rst | 38 ++++ .../core/sinks/common/channel_transformer.py | 62 +++---- .../core/sinks/msteams/msteams_sink.py | 3 +- .../core/sinks/msteams/msteams_sink_params.py | 10 ++ .../msteams/msteams_webhook_tranformer.py | 69 ++++++++ src/robusta/integrations/msteams/sender.py | 12 +- tests/test_ms_teams_transformer.py | 165 ++++++++++++++++++ 7 files changed, 324 insertions(+), 35 deletions(-) create mode 100644 src/robusta/core/sinks/msteams/msteams_webhook_tranformer.py create mode 100644 tests/test_ms_teams_transformer.py diff --git a/docs/configuration/sinks/ms-teams.rst b/docs/configuration/sinks/ms-teams.rst index 133961a97..52b10623f 100644 --- a/docs/configuration/sinks/ms-teams.rst +++ b/docs/configuration/sinks/ms-teams.rst @@ -20,6 +20,7 @@ Configuring the MS Teams sink - ms_teams_sink: name: main_ms_teams_sink webhook_url: teams-incoming-webhook # see instructions below + webhook_override: DYNAMIC MS TEAMS WEBHOOK URL OVERRIDE (Optional) Then do a :ref:`Helm Upgrade `. @@ -35,3 +36,40 @@ Obtaining a webhook URL .. image:: /images/msteams_sink/msteam_get_webhook_url.gif :width: 1024 :align: center + + +Dynamically Route MS Teams Alerts +------------------------------------------------------------------- + +You can set the MS Teams webhook url value dynamically, based on the value of a specific ``annotation`` and environmental variable passed to runner. + +This can be done using the optional ``webhook_override`` sink parameter. + +As for now, the ``webhook_override`` parameter supports retrieving values specifically from annotations. You can specify an annotation key to retrieve the MS Teams webhook URL using the format ``annotations.``. For example, if you use ``annotations.ms-team-alerts-sink``, the webhook URL will be taken from an annotation with the key ``ms-team-alerts-sink``. + +If the specified annotation does not exist, the default webhook URL from the ``webhook_url`` parameter will be used. If the annotation exists but does not contain a URL, the system will look for an environmental variable with the name matching the ``annotation`` value. + +.. code-block:: yaml + + sinksConfig: + # MS Teams integration params + - ms_teams_sink: + name: main_ms_teams_sink + webhook_url: teams-incoming-webhook # see instructions below + webhook_override: "annotations.ms-team-alerts-sink" + +A replacement pattern is also allowed, using ``$`` sign, before the variable. +For cases where labels or annotations include special characters, such as ``${annotations.kubernetes.io/service-name}``, you can use the `${}` replacement pattern to represent the entire key, including special characters. +For example, if you want to dynamically set the MS Teams webhook url based on the annotation ``kubernetes.io/service-name``, you can use the following syntax: + +- ``webhook_override: "${annotations.kubernetes.io/service-name}"`` + +Example: + +.. code-block:: yaml + + sinksConfig: + - ms_teams_sink: + name: main_ms_teams_sink + webhook_url: teams-incoming-webhook # see instructions below + webhook_override: ${annotations.kubernetes.io/service-name} diff --git a/src/robusta/core/sinks/common/channel_transformer.py b/src/robusta/core/sinks/common/channel_transformer.py index a6300fb45..bec5c3ce9 100644 --- a/src/robusta/core/sinks/common/channel_transformer.py +++ b/src/robusta/core/sinks/common/channel_transformer.py @@ -1,6 +1,6 @@ from collections import defaultdict from string import Template -from typing import Dict, Optional, Union +from typing import Dict, Optional import regex @@ -18,21 +18,7 @@ MISSING = "" -class ChannelTransformer: - @classmethod - def validate_channel_override(cls, v: Union[str, None]): - if v: - if regex.match(ONLY_VALUE_PATTERN, v): - return "$" + v - if not regex.match(COMPOSITE_PATTERN, v): - err_msg = ( - f"channel_override must be '{CLUSTER_PREF}' or '{LABELS_PREF}foo' or '{ANNOTATIONS_PREF}foo' " - f"or contain patters like: '${CLUSTER_PREF}'/'${LABELS_PREF}foo'/" - f"'${ANNOTATIONS_PREF}foo'" - ) - raise ValueError(err_msg) - return v - +class BaseChannelTransformer: @classmethod def normalize_key_string(cls, s: str) -> str: return s.replace("/", "_").replace(".", "_").replace("-", "_") @@ -47,7 +33,7 @@ def normalize_dict_keys(cls, metadata: Dict) -> Dict: # else, if found, return replacement else return MISSING @classmethod def get_replacement(cls, prefix: str, value: str, normalized_replacements: Dict) -> str: - if prefix in value: # value is in the format of "$prefix" or "prefix" + if prefix in value: value = cls.normalize_key_string(value.replace(prefix, "")) if "$" in value: return Template(value).safe_substitute(normalized_replacements) @@ -56,13 +42,7 @@ def get_replacement(cls, prefix: str, value: str, normalized_replacements: Dict) return "" @classmethod - def replace_token( - cls, - pattern: regex.Pattern, - prefix: str, - channel: str, - replacements: Dict[str, str], - ) -> str: + def replace_token(cls, pattern: regex.Pattern, prefix: str, channel: str, replacements: Dict[str, str]) -> str: tokens = pattern.findall(channel) for token in tokens: clean_token = token.replace("{", "").replace("}", "") @@ -71,6 +51,30 @@ def replace_token( channel = channel.replace(token, replacement) return channel + @classmethod + def process_template_annotations(cls, channel: str, annotations: Dict[str, str]) -> str: + if ANNOTATIONS_PREF in channel: + normalized_annotations = cls.normalize_dict_keys(annotations) + channel = cls.replace_token(BRACKETS_PATTERN, ANNOTATIONS_PREF, channel, normalized_annotations) + channel = cls.replace_token(ANNOTATIONS_PREF_PATTERN, ANNOTATIONS_PREF, channel, normalized_annotations) + return channel + + +class ChannelTransformer(BaseChannelTransformer): + @classmethod + def validate_channel_override(cls, v: Optional[str]) -> str: + if v: + if regex.match(ONLY_VALUE_PATTERN, v): + return "$" + v + if not regex.match(COMPOSITE_PATTERN, v): + err_msg = ( + f"channel_override must be '{CLUSTER_PREF}' or '{LABELS_PREF}foo' or '{ANNOTATIONS_PREF}foo' " + f"or contain patters like: '${CLUSTER_PREF}'/'${LABELS_PREF}foo'/" + f"'${ANNOTATIONS_PREF}foo'" + ) + raise ValueError(err_msg) + return v + @classmethod def template( cls, @@ -93,14 +97,6 @@ def template( channel = cls.replace_token(BRACKETS_PATTERN, LABELS_PREF, channel, normalized_labels) channel = cls.replace_token(LABEL_PREF_PATTERN, LABELS_PREF, channel, normalized_labels) - if ANNOTATIONS_PREF in channel: - normalized_annotations = cls.normalize_dict_keys(annotations) - channel = cls.replace_token(BRACKETS_PATTERN, ANNOTATIONS_PREF, channel, normalized_annotations) - channel = cls.replace_token( - ANNOTATIONS_PREF_PATTERN, - ANNOTATIONS_PREF, - channel, - normalized_annotations, - ) + channel = cls.process_template_annotations(channel, annotations) return channel if MISSING not in channel else default_channel diff --git a/src/robusta/core/sinks/msteams/msteams_sink.py b/src/robusta/core/sinks/msteams/msteams_sink.py index c1783260c..423691ad4 100644 --- a/src/robusta/core/sinks/msteams/msteams_sink.py +++ b/src/robusta/core/sinks/msteams/msteams_sink.py @@ -8,8 +8,9 @@ class MsTeamsSink(SinkBase): def __init__(self, sink_config: MsTeamsSinkConfigWrapper, registry): super().__init__(sink_config.ms_teams_sink, registry) self.webhook_url = sink_config.ms_teams_sink.webhook_url + self.webhook_override = sink_config.ms_teams_sink.webhook_override def write_finding(self, finding: Finding, platform_enabled: bool): MsTeamsSender.send_finding_to_ms_teams( - self.webhook_url, finding, platform_enabled, self.cluster_name, self.account_id + self.webhook_url, finding, platform_enabled, self.cluster_name, self.account_id, self.webhook_override ) diff --git a/src/robusta/core/sinks/msteams/msteams_sink_params.py b/src/robusta/core/sinks/msteams/msteams_sink_params.py index 826c0ce27..b10ba7ea0 100644 --- a/src/robusta/core/sinks/msteams/msteams_sink_params.py +++ b/src/robusta/core/sinks/msteams/msteams_sink_params.py @@ -1,14 +1,24 @@ +from typing import Optional + +from pydantic import validator + +from robusta.core.sinks.msteams.msteams_webhook_tranformer import MsTeamsWebhookUrlTransformer from robusta.core.sinks.sink_base_params import SinkBaseParams from robusta.core.sinks.sink_config import SinkConfigBase class MsTeamsSinkParams(SinkBaseParams): webhook_url: str + webhook_override: Optional[str] = None @classmethod def _get_sink_type(cls): return "msteams" + @validator("webhook_override") + def validate_webhook_override(cls, v: str): + return MsTeamsWebhookUrlTransformer.validate_webhook_override(v) + class MsTeamsSinkConfigWrapper(SinkConfigBase): ms_teams_sink: MsTeamsSinkParams diff --git a/src/robusta/core/sinks/msteams/msteams_webhook_tranformer.py b/src/robusta/core/sinks/msteams/msteams_webhook_tranformer.py new file mode 100644 index 000000000..9c467a2d8 --- /dev/null +++ b/src/robusta/core/sinks/msteams/msteams_webhook_tranformer.py @@ -0,0 +1,69 @@ +import logging +import os +from typing import Dict, Optional + +import regex + +from robusta.core.sinks.common.channel_transformer import ANNOTATIONS_PREF, MISSING, BaseChannelTransformer + +ANNOTATIONS_COMPOSITE_PATTERN = r".*\$({?annotations.[^$]+).*" +ANNOTATIONS_ONLY_VALUE_PATTERN = r"^(annotations.[^$]+)$" +URL_PATTERN = regex.compile( + r"^(https?)://" + r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|" + r"localhost|" + r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|" + r"\[?[A-F0-9]*:[A-F0-9:]+\]?)" + r"(?::\d+)?" + r"(?:/?|[/?]\S+)$", + regex.IGNORECASE, +) + + +# This class supports overriding the webhook_url only using annotations from yaml files. +# Annotations are used instead of labels because urls can be passed to annotations contrary to labels. +# Labels must be an empty string or consist of alphanumeric characters, '-', '_', or '.', +# and must start and end with an alphanumeric character (e.g., 'MyValue', 'my_value', or '12345'). +# The regex used for label validation is '(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?'. +class MsTeamsWebhookUrlTransformer(BaseChannelTransformer): + @classmethod + def validate_webhook_override(cls, v: Optional[str]) -> Optional[str]: + if v: + if regex.match(ANNOTATIONS_ONLY_VALUE_PATTERN, v): + return "$" + v + if not regex.match(ANNOTATIONS_COMPOSITE_PATTERN, v): + err_msg = f"webhook_override must be '{ANNOTATIONS_PREF}foo' or contain patterns like: '${ANNOTATIONS_PREF}foo'" + raise ValueError(err_msg) + return v + + @classmethod + def validate_url_or_get_env(cls, webhook_url: str, default_webhook_url: str) -> str: + if URL_PATTERN.match(webhook_url): + return webhook_url + logging.info(f"URL matching failed for: {webhook_url}. Trying to get environment variable.") + + env_value = os.getenv(webhook_url) + if env_value: + return env_value + logging.info(f"Environment variable not found for: {webhook_url}. Using default webhook URL.") + + return default_webhook_url + + @classmethod + def template( + cls, + webhook_override: Optional[str], + default_webhook_url: str, + annotations: Dict[str, str], + ) -> str: + if not webhook_override: + return default_webhook_url + + webhook_url = webhook_override + + webhook_url = cls.process_template_annotations(webhook_url, annotations) + if MISSING in webhook_url: + return default_webhook_url + webhook_url = cls.validate_url_or_get_env(webhook_url, default_webhook_url) + + return webhook_url diff --git a/src/robusta/integrations/msteams/sender.py b/src/robusta/integrations/msteams/sender.py index 96c74f557..1e2db511c 100644 --- a/src/robusta/integrations/msteams/sender.py +++ b/src/robusta/integrations/msteams/sender.py @@ -13,6 +13,7 @@ MarkdownBlock, TableBlock, ) +from robusta.core.sinks.msteams.msteams_webhook_tranformer import MsTeamsWebhookUrlTransformer from robusta.integrations.msteams.msteams_msg import MsTeamsMsg @@ -50,8 +51,17 @@ def __split_block_to_files_and_all_the_rest(cls, enrichment: Enrichment): @classmethod def send_finding_to_ms_teams( - cls, webhook_url: str, finding: Finding, platform_enabled: bool, cluster_name: str, account_id: str + cls, + webhook_url: str, + finding: Finding, + platform_enabled: bool, + cluster_name: str, + account_id: str, + webhook_override: str, ): + webhook_url = MsTeamsWebhookUrlTransformer.template( + webhook_override=webhook_override, default_webhook_url=webhook_url, annotations=finding.subject.annotations + ) msg = MsTeamsMsg(webhook_url) msg.write_title_and_desc(platform_enabled, finding, cluster_name, account_id) diff --git a/tests/test_ms_teams_transformer.py b/tests/test_ms_teams_transformer.py new file mode 100644 index 000000000..30c6caa70 --- /dev/null +++ b/tests/test_ms_teams_transformer.py @@ -0,0 +1,165 @@ +from unittest.mock import patch + +import pytest + +from robusta.core.sinks.msteams.msteams_webhook_tranformer import MsTeamsWebhookUrlTransformer + +DEFAULT_WEBHOOK_URL = "http://example-default-webhook.com" +OVERRIDE_WEBHOOK_URL = "http://example.com" +OVERRIDE_CONTAINING_ENV_NAME = "WEBHOOK_VALUE" +ENV_MOCK_WITH_EMPTY_WEBHOOK_VALUE = None +ENV_MOCK_WITH_WEBHOOK_URL = "http://from-env-example.com" +VALID_URLS_LIST = [ + "http://example.com", + "https://example.com", + "http://127.0.0.1", + "http://127.0.0.1:8080", + "http://example.com/path?query=string", + "https://example.com:443/path/to/resource?query=param#fragment", +] +INVALID_URLS_LIST = [ + "example.com", + "ftp://example.com", + "http://example", + "http://example.com/path with spaces", + "http://-example.com", +] + + +@pytest.mark.parametrize( + "webhook_override, annotations, expected, error, env_value", + [ + ( + "annotations.msteams", + {"msteams": OVERRIDE_WEBHOOK_URL}, + OVERRIDE_WEBHOOK_URL, + "override channel not found", + ENV_MOCK_WITH_EMPTY_WEBHOOK_VALUE, + ), + ( + "annotations.msteams", + {"msteams": OVERRIDE_CONTAINING_ENV_NAME}, + ENV_MOCK_WITH_WEBHOOK_URL, + "env with webhook value is not found", + ENV_MOCK_WITH_WEBHOOK_URL, + ), + ( + "annotations.msteams", + {"msteam": OVERRIDE_WEBHOOK_URL}, + DEFAULT_WEBHOOK_URL, + "override - default channel not found", + ENV_MOCK_WITH_EMPTY_WEBHOOK_VALUE, + ), + ( + "$annotations.msteams", + {"msteams": OVERRIDE_WEBHOOK_URL}, + OVERRIDE_WEBHOOK_URL, + "override - default channel not chosen", + ENV_MOCK_WITH_EMPTY_WEBHOOK_VALUE, + ), + ( + "$annotations.msteams", + {"variable": OVERRIDE_WEBHOOK_URL}, + DEFAULT_WEBHOOK_URL, + "override - default channel not chosen", + ENV_MOCK_WITH_EMPTY_WEBHOOK_VALUE, + ), + ( + "${annotations.kubernetes.io/service-name}", + {"kubernetes.io/service-name": OVERRIDE_WEBHOOK_URL}, + OVERRIDE_WEBHOOK_URL, + "override channel not found", + ENV_MOCK_WITH_EMPTY_WEBHOOK_VALUE, + ), + ( + "${annotations.kubernetes.io/service-name}", + {"kubernetes.io/service": OVERRIDE_WEBHOOK_URL}, + DEFAULT_WEBHOOK_URL, + "override - default channel not chosen", + ENV_MOCK_WITH_EMPTY_WEBHOOK_VALUE, + ), + ( + "${annotations.kubernetes.io/service-name}", + {}, + DEFAULT_WEBHOOK_URL, + "override - default channel not chosen", + ENV_MOCK_WITH_EMPTY_WEBHOOK_VALUE, + ), + ( + "$cluster_name-alerts-$annotations.env-${annotations.kubernetes.io/service-name}", + {"kubernetes.io/service-name": "yyy"}, + DEFAULT_WEBHOOK_URL, + "override channel not found", + ENV_MOCK_WITH_EMPTY_WEBHOOK_VALUE, + ), + ( + "$cluster_name-alerts-$annotations.env-${annotations.kubernetes.io/service-name}", + {"kubernetes.io/service-name": "yyy"}, + DEFAULT_WEBHOOK_URL, + "override - default channel not chosen", + ENV_MOCK_WITH_EMPTY_WEBHOOK_VALUE, + ), + ], +) +def test_ms_teams_webhook_transformer_template_method(webhook_override, annotations, expected, error, env_value): + with patch("robusta.core.sinks.msteams.msteams_webhook_tranformer.os.getenv", return_value=env_value): + webhook_url = MsTeamsWebhookUrlTransformer.template( + webhook_override=webhook_override, + default_webhook_url=DEFAULT_WEBHOOK_URL, + annotations=annotations, + ) + assert webhook_url == expected, f"{webhook_override} {error}" + + +@pytest.mark.parametrize( + "webhook_override, env_value, expected, error", + [ + (VALID_URLS_LIST[0], ENV_MOCK_WITH_EMPTY_WEBHOOK_VALUE, VALID_URLS_LIST[0], "webhook url is not valid"), + (VALID_URLS_LIST[1], ENV_MOCK_WITH_EMPTY_WEBHOOK_VALUE, VALID_URLS_LIST[1], "webhook url is not valid"), + (VALID_URLS_LIST[2], ENV_MOCK_WITH_EMPTY_WEBHOOK_VALUE, VALID_URLS_LIST[2], "webhook url is not valid"), + (VALID_URLS_LIST[3], ENV_MOCK_WITH_EMPTY_WEBHOOK_VALUE, VALID_URLS_LIST[3], "webhook url is not valid"), + (VALID_URLS_LIST[4], ENV_MOCK_WITH_EMPTY_WEBHOOK_VALUE, VALID_URLS_LIST[4], "webhook url is not valid"), + (VALID_URLS_LIST[5], ENV_MOCK_WITH_EMPTY_WEBHOOK_VALUE, VALID_URLS_LIST[5], "webhook url is not valid"), + (INVALID_URLS_LIST[0], ENV_MOCK_WITH_WEBHOOK_URL, ENV_MOCK_WITH_WEBHOOK_URL, "webhook url is not valid"), + (INVALID_URLS_LIST[1], ENV_MOCK_WITH_WEBHOOK_URL, ENV_MOCK_WITH_WEBHOOK_URL, "webhook url is not valid"), + (INVALID_URLS_LIST[2], ENV_MOCK_WITH_WEBHOOK_URL, ENV_MOCK_WITH_WEBHOOK_URL, "webhook url is not valid"), + (INVALID_URLS_LIST[3], ENV_MOCK_WITH_WEBHOOK_URL, ENV_MOCK_WITH_WEBHOOK_URL, "webhook url is not valid"), + (INVALID_URLS_LIST[4], ENV_MOCK_WITH_WEBHOOK_URL, ENV_MOCK_WITH_WEBHOOK_URL, "webhook url is not valid"), + ], +) +def test_ms_teams_webhook_transformer_validate_url_or_get_env_method(webhook_override, expected, env_value, error): + with patch("robusta.core.sinks.msteams.msteams_webhook_tranformer.os.getenv", return_value=env_value): + webhook_url = MsTeamsWebhookUrlTransformer.validate_url_or_get_env( + webhook_url=webhook_override, default_webhook_url=DEFAULT_WEBHOOK_URL + ) + assert webhook_url == expected, f"{webhook_override} {error}" + + +@pytest.mark.parametrize( + "webhook_override, expected, error", + [ + ("annotations.team", "$annotations.team", "missing '$' prefix"), + ("$annotations.team", "$annotations.team", "override should be left unchanged"), + ("${annotations.team}", "${annotations.team}", "override should be left unchanged"), + ], +) +def test_ms_teams_webhook_transformer_validate_webhook_override_method(webhook_override, expected, error): + webhook_url = MsTeamsWebhookUrlTransformer.validate_webhook_override(webhook_override) + assert webhook_url == expected, f"{webhook_override} {error}" + + +@pytest.mark.parametrize( + "webhook_override", + [ + "cluste_name", + "annotations.", + "$annotations.", + "invalid.something", + "labels.", + "$labels.", + "test", + ], +) +def test_ms_teams_webhook_transformer_validate_webhook_override_raises_error(webhook_override): + with pytest.raises(ValueError): + MsTeamsWebhookUrlTransformer.validate_webhook_override(webhook_override) From fcc8658d695e473dd09f03a12b726b69ab837eaf Mon Sep 17 00:00:00 2001 From: Tomer Date: Sun, 7 Jul 2024 15:00:56 +0300 Subject: [PATCH 4/8] deploying docs to robusta-docs instead of robusta-public (#1490) --- .github/workflows/deploy-docs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-docs.yaml b/.github/workflows/deploy-docs.yaml index e5c55e8c0..d7d7adabf 100644 --- a/.github/workflows/deploy-docs.yaml +++ b/.github/workflows/deploy-docs.yaml @@ -74,7 +74,7 @@ jobs: - name: Upload docs to public gcp bucket if: needs.has-secrets.outputs.docs - run: gsutil rsync -R ./docs/_build/html "gs://robusta-public/${GITHUB_REF##*/}/" + run: gsutil rsync -R ./docs/_build/html "gs://robusta-docs/${GITHUB_REF##*/}/" - name: Upload docs artifact uses: actions/upload-artifact@v2 From d6d72308431bece7a5504fbc529cf4e7c45aee05 Mon Sep 17 00:00:00 2001 From: Robert Szefler Date: Tue, 9 Jul 2024 09:45:16 +0200 Subject: [PATCH 5/8] Fix markdown block quote truncation (#1457) Fix markdown block quote truncation --- poetry.lock | 484 ++++++++++++----------- pyproject.toml | 2 + src/robusta/core/sinks/transformer.py | 9 +- src/robusta/integrations/slack/sender.py | 2 +- src/robusta/utils/trim_markdown.py | 57 +++ tests/test_trim_markdown.py | 71 ++++ 6 files changed, 394 insertions(+), 231 deletions(-) create mode 100644 src/robusta/utils/trim_markdown.py create mode 100644 tests/test_trim_markdown.py diff --git a/poetry.lock b/poetry.lock index 423aa4c89..b9b3819b3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "absolufy-imports" @@ -283,13 +283,13 @@ files = [ [[package]] name = "cairocffi" -version = "1.7.0" +version = "1.7.1" description = "cffi-based cairo bindings for Python" optional = true python-versions = ">=3.8" files = [ - {file = "cairocffi-1.7.0-py3-none-any.whl", hash = "sha256:1f29a8d41dbda4090c0aa33bcdea64f3b493e95f74a43ea107c4a8a7b7f632ef"}, - {file = "cairocffi-1.7.0.tar.gz", hash = "sha256:7761863603894305f3160eca68452f373433ca8745ab7dd445bd2c6ce50dcab7"}, + {file = "cairocffi-1.7.1-py3-none-any.whl", hash = "sha256:9803a0e11f6c962f3b0ae2ec8ba6ae45e957a146a004697a1ac1bbf16b073b3f"}, + {file = "cairocffi-1.7.1.tar.gz", hash = "sha256:2e48ee864884ec4a3a34bfa8c9ab9999f688286eb714a15a43ec9d068c36557b"}, ] [package.dependencies] @@ -822,13 +822,13 @@ files = [ [[package]] name = "dpath" -version = "2.1.6" +version = "2.2.0" description = "Filesystem-like pathing and searching for dictionaries" optional = false python-versions = ">=3.7" files = [ - {file = "dpath-2.1.6-py3-none-any.whl", hash = "sha256:31407395b177ab63ef72e2f6ae268c15e938f2990a8ecf6510f5686c02b6db73"}, - {file = "dpath-2.1.6.tar.gz", hash = "sha256:f1e07c72e8605c6a9e80b64bc8f42714de08a789c7de417e49c3f87a19692e47"}, + {file = "dpath-2.2.0-py3-none-any.whl", hash = "sha256:b330a375ded0a0d2ed404440f6c6a715deae5313af40bbb01c8a41d891900576"}, + {file = "dpath-2.2.0.tar.gz", hash = "sha256:34f7e630dc55ea3f219e555726f5da4b4b25f2200319c8e6902c394258dd6a3e"}, ] [[package]] @@ -847,18 +847,18 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.14.0" +version = "3.15.4" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, - {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, + {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, + {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] typing = ["typing-extensions (>=4.8)"] [[package]] @@ -886,53 +886,53 @@ dotenv = ["python-dotenv"] [[package]] name = "fonttools" -version = "4.53.0" +version = "4.53.1" description = "Tools to manipulate font files" optional = false python-versions = ">=3.8" files = [ - {file = "fonttools-4.53.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:52a6e0a7a0bf611c19bc8ec8f7592bdae79c8296c70eb05917fd831354699b20"}, - {file = "fonttools-4.53.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:099634631b9dd271d4a835d2b2a9e042ccc94ecdf7e2dd9f7f34f7daf333358d"}, - {file = "fonttools-4.53.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e40013572bfb843d6794a3ce076c29ef4efd15937ab833f520117f8eccc84fd6"}, - {file = "fonttools-4.53.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:715b41c3e231f7334cbe79dfc698213dcb7211520ec7a3bc2ba20c8515e8a3b5"}, - {file = "fonttools-4.53.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74ae2441731a05b44d5988d3ac2cf784d3ee0a535dbed257cbfff4be8bb49eb9"}, - {file = "fonttools-4.53.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:95db0c6581a54b47c30860d013977b8a14febc206c8b5ff562f9fe32738a8aca"}, - {file = "fonttools-4.53.0-cp310-cp310-win32.whl", hash = "sha256:9cd7a6beec6495d1dffb1033d50a3f82dfece23e9eb3c20cd3c2444d27514068"}, - {file = "fonttools-4.53.0-cp310-cp310-win_amd64.whl", hash = "sha256:daaef7390e632283051e3cf3e16aff2b68b247e99aea916f64e578c0449c9c68"}, - {file = "fonttools-4.53.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a209d2e624ba492df4f3bfad5996d1f76f03069c6133c60cd04f9a9e715595ec"}, - {file = "fonttools-4.53.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4f520d9ac5b938e6494f58a25c77564beca7d0199ecf726e1bd3d56872c59749"}, - {file = "fonttools-4.53.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eceef49f457253000e6a2d0f7bd08ff4e9fe96ec4ffce2dbcb32e34d9c1b8161"}, - {file = "fonttools-4.53.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa1f3e34373aa16045484b4d9d352d4c6b5f9f77ac77a178252ccbc851e8b2ee"}, - {file = "fonttools-4.53.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:28d072169fe8275fb1a0d35e3233f6df36a7e8474e56cb790a7258ad822b6fd6"}, - {file = "fonttools-4.53.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4a2a6ba400d386e904fd05db81f73bee0008af37799a7586deaa4aef8cd5971e"}, - {file = "fonttools-4.53.0-cp311-cp311-win32.whl", hash = "sha256:bb7273789f69b565d88e97e9e1da602b4ee7ba733caf35a6c2affd4334d4f005"}, - {file = "fonttools-4.53.0-cp311-cp311-win_amd64.whl", hash = "sha256:9fe9096a60113e1d755e9e6bda15ef7e03391ee0554d22829aa506cdf946f796"}, - {file = "fonttools-4.53.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d8f191a17369bd53a5557a5ee4bab91d5330ca3aefcdf17fab9a497b0e7cff7a"}, - {file = "fonttools-4.53.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:93156dd7f90ae0a1b0e8871032a07ef3178f553f0c70c386025a808f3a63b1f4"}, - {file = "fonttools-4.53.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bff98816cb144fb7b85e4b5ba3888a33b56ecef075b0e95b95bcd0a5fbf20f06"}, - {file = "fonttools-4.53.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:973d030180eca8255b1bce6ffc09ef38a05dcec0e8320cc9b7bcaa65346f341d"}, - {file = "fonttools-4.53.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c4ee5a24e281fbd8261c6ab29faa7fd9a87a12e8c0eed485b705236c65999109"}, - {file = "fonttools-4.53.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bd5bc124fae781a4422f61b98d1d7faa47985f663a64770b78f13d2c072410c2"}, - {file = "fonttools-4.53.0-cp312-cp312-win32.whl", hash = "sha256:a239afa1126b6a619130909c8404070e2b473dd2b7fc4aacacd2e763f8597fea"}, - {file = "fonttools-4.53.0-cp312-cp312-win_amd64.whl", hash = "sha256:45b4afb069039f0366a43a5d454bc54eea942bfb66b3fc3e9a2c07ef4d617380"}, - {file = "fonttools-4.53.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:93bc9e5aaa06ff928d751dc6be889ff3e7d2aa393ab873bc7f6396a99f6fbb12"}, - {file = "fonttools-4.53.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2367d47816cc9783a28645bc1dac07f8ffc93e0f015e8c9fc674a5b76a6da6e4"}, - {file = "fonttools-4.53.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:907fa0b662dd8fc1d7c661b90782ce81afb510fc4b7aa6ae7304d6c094b27bce"}, - {file = "fonttools-4.53.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e0ad3c6ea4bd6a289d958a1eb922767233f00982cf0fe42b177657c86c80a8f"}, - {file = "fonttools-4.53.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:73121a9b7ff93ada888aaee3985a88495489cc027894458cb1a736660bdfb206"}, - {file = "fonttools-4.53.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ee595d7ba9bba130b2bec555a40aafa60c26ce68ed0cf509983e0f12d88674fd"}, - {file = "fonttools-4.53.0-cp38-cp38-win32.whl", hash = "sha256:fca66d9ff2ac89b03f5aa17e0b21a97c21f3491c46b583bb131eb32c7bab33af"}, - {file = "fonttools-4.53.0-cp38-cp38-win_amd64.whl", hash = "sha256:31f0e3147375002aae30696dd1dc596636abbd22fca09d2e730ecde0baad1d6b"}, - {file = "fonttools-4.53.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7d6166192dcd925c78a91d599b48960e0a46fe565391c79fe6de481ac44d20ac"}, - {file = "fonttools-4.53.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef50ec31649fbc3acf6afd261ed89d09eb909b97cc289d80476166df8438524d"}, - {file = "fonttools-4.53.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f193f060391a455920d61684a70017ef5284ccbe6023bb056e15e5ac3de11d1"}, - {file = "fonttools-4.53.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba9f09ff17f947392a855e3455a846f9855f6cf6bec33e9a427d3c1d254c712f"}, - {file = "fonttools-4.53.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0c555e039d268445172b909b1b6bdcba42ada1cf4a60e367d68702e3f87e5f64"}, - {file = "fonttools-4.53.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5a4788036201c908079e89ae3f5399b33bf45b9ea4514913f4dbbe4fac08efe0"}, - {file = "fonttools-4.53.0-cp39-cp39-win32.whl", hash = "sha256:d1a24f51a3305362b94681120c508758a88f207fa0a681c16b5a4172e9e6c7a9"}, - {file = "fonttools-4.53.0-cp39-cp39-win_amd64.whl", hash = "sha256:1e677bfb2b4bd0e5e99e0f7283e65e47a9814b0486cb64a41adf9ef110e078f2"}, - {file = "fonttools-4.53.0-py3-none-any.whl", hash = "sha256:6b4f04b1fbc01a3569d63359f2227c89ab294550de277fd09d8fca6185669fa4"}, - {file = "fonttools-4.53.0.tar.gz", hash = "sha256:c93ed66d32de1559b6fc348838c7572d5c0ac1e4a258e76763a5caddd8944002"}, + {file = "fonttools-4.53.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0679a30b59d74b6242909945429dbddb08496935b82f91ea9bf6ad240ec23397"}, + {file = "fonttools-4.53.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8bf06b94694251861ba7fdeea15c8ec0967f84c3d4143ae9daf42bbc7717fe3"}, + {file = "fonttools-4.53.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b96cd370a61f4d083c9c0053bf634279b094308d52fdc2dd9a22d8372fdd590d"}, + {file = "fonttools-4.53.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1c7c5aa18dd3b17995898b4a9b5929d69ef6ae2af5b96d585ff4005033d82f0"}, + {file = "fonttools-4.53.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e013aae589c1c12505da64a7d8d023e584987e51e62006e1bb30d72f26522c41"}, + {file = "fonttools-4.53.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9efd176f874cb6402e607e4cc9b4a9cd584d82fc34a4b0c811970b32ba62501f"}, + {file = "fonttools-4.53.1-cp310-cp310-win32.whl", hash = "sha256:c8696544c964500aa9439efb6761947393b70b17ef4e82d73277413f291260a4"}, + {file = "fonttools-4.53.1-cp310-cp310-win_amd64.whl", hash = "sha256:8959a59de5af6d2bec27489e98ef25a397cfa1774b375d5787509c06659b3671"}, + {file = "fonttools-4.53.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:da33440b1413bad53a8674393c5d29ce64d8c1a15ef8a77c642ffd900d07bfe1"}, + {file = "fonttools-4.53.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ff7e5e9bad94e3a70c5cd2fa27f20b9bb9385e10cddab567b85ce5d306ea923"}, + {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6e7170d675d12eac12ad1a981d90f118c06cf680b42a2d74c6c931e54b50719"}, + {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee32ea8765e859670c4447b0817514ca79054463b6b79784b08a8df3a4d78e3"}, + {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6e08f572625a1ee682115223eabebc4c6a2035a6917eac6f60350aba297ccadb"}, + {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b21952c092ffd827504de7e66b62aba26fdb5f9d1e435c52477e6486e9d128b2"}, + {file = "fonttools-4.53.1-cp311-cp311-win32.whl", hash = "sha256:9dfdae43b7996af46ff9da520998a32b105c7f098aeea06b2226b30e74fbba88"}, + {file = "fonttools-4.53.1-cp311-cp311-win_amd64.whl", hash = "sha256:d4d0096cb1ac7a77b3b41cd78c9b6bc4a400550e21dc7a92f2b5ab53ed74eb02"}, + {file = "fonttools-4.53.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d92d3c2a1b39631a6131c2fa25b5406855f97969b068e7e08413325bc0afba58"}, + {file = "fonttools-4.53.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3b3c8ebafbee8d9002bd8f1195d09ed2bd9ff134ddec37ee8f6a6375e6a4f0e8"}, + {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32f029c095ad66c425b0ee85553d0dc326d45d7059dbc227330fc29b43e8ba60"}, + {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f5e6c3510b79ea27bb1ebfcc67048cde9ec67afa87c7dd7efa5c700491ac7f"}, + {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f677ce218976496a587ab17140da141557beb91d2a5c1a14212c994093f2eae2"}, + {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9e6ceba2a01b448e36754983d376064730690401da1dd104ddb543519470a15f"}, + {file = "fonttools-4.53.1-cp312-cp312-win32.whl", hash = "sha256:791b31ebbc05197d7aa096bbc7bd76d591f05905d2fd908bf103af4488e60670"}, + {file = "fonttools-4.53.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ed170b5e17da0264b9f6fae86073be3db15fa1bd74061c8331022bca6d09bab"}, + {file = "fonttools-4.53.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c818c058404eb2bba05e728d38049438afd649e3c409796723dfc17cd3f08749"}, + {file = "fonttools-4.53.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:651390c3b26b0c7d1f4407cad281ee7a5a85a31a110cbac5269de72a51551ba2"}, + {file = "fonttools-4.53.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e54f1bba2f655924c1138bbc7fa91abd61f45c68bd65ab5ed985942712864bbb"}, + {file = "fonttools-4.53.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9cd19cf4fe0595ebdd1d4915882b9440c3a6d30b008f3cc7587c1da7b95be5f"}, + {file = "fonttools-4.53.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2af40ae9cdcb204fc1d8f26b190aa16534fcd4f0df756268df674a270eab575d"}, + {file = "fonttools-4.53.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:35250099b0cfb32d799fb5d6c651220a642fe2e3c7d2560490e6f1d3f9ae9169"}, + {file = "fonttools-4.53.1-cp38-cp38-win32.whl", hash = "sha256:f08df60fbd8d289152079a65da4e66a447efc1d5d5a4d3f299cdd39e3b2e4a7d"}, + {file = "fonttools-4.53.1-cp38-cp38-win_amd64.whl", hash = "sha256:7b6b35e52ddc8fb0db562133894e6ef5b4e54e1283dff606fda3eed938c36fc8"}, + {file = "fonttools-4.53.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75a157d8d26c06e64ace9df037ee93a4938a4606a38cb7ffaf6635e60e253b7a"}, + {file = "fonttools-4.53.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4824c198f714ab5559c5be10fd1adf876712aa7989882a4ec887bf1ef3e00e31"}, + {file = "fonttools-4.53.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:becc5d7cb89c7b7afa8321b6bb3dbee0eec2b57855c90b3e9bf5fb816671fa7c"}, + {file = "fonttools-4.53.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ec3fb43befb54be490147b4a922b5314e16372a643004f182babee9f9c3407"}, + {file = "fonttools-4.53.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:73379d3ffdeecb376640cd8ed03e9d2d0e568c9d1a4e9b16504a834ebadc2dfb"}, + {file = "fonttools-4.53.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:02569e9a810f9d11f4ae82c391ebc6fb5730d95a0657d24d754ed7763fb2d122"}, + {file = "fonttools-4.53.1-cp39-cp39-win32.whl", hash = "sha256:aae7bd54187e8bf7fd69f8ab87b2885253d3575163ad4d669a262fe97f0136cb"}, + {file = "fonttools-4.53.1-cp39-cp39-win_amd64.whl", hash = "sha256:e5b708073ea3d684235648786f5f6153a48dc8762cdfe5563c57e80787c29fbb"}, + {file = "fonttools-4.53.1-py3-none-any.whl", hash = "sha256:f1f8758a2ad110bd6432203a344269f445a2907dc24ef6bccfd0ac4e14e0d71d"}, + {file = "fonttools-4.53.1.tar.gz", hash = "sha256:e128778a8e9bc11159ce5447f76766cefbd876f44bd79aff030287254e4752c4"}, ] [package.extras] @@ -992,13 +992,13 @@ files = [ [[package]] name = "google-auth" -version = "2.30.0" +version = "2.31.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google-auth-2.30.0.tar.gz", hash = "sha256:ab630a1320f6720909ad76a7dbdb6841cdf5c66b328d690027e4867bdfb16688"}, - {file = "google_auth-2.30.0-py2.py3-none-any.whl", hash = "sha256:8df7da660f62757388b8a7f249df13549b3373f24388cb5d2f1dd91cc18180b5"}, + {file = "google-auth-2.31.0.tar.gz", hash = "sha256:87805c36970047247c8afe614d4e3af8eceafc1ebba0c679fe75ddd1d575e871"}, + {file = "google_auth-2.31.0-py2.py3-none-any.whl", hash = "sha256:042c4702efa9f7d3c48d3a69341c209381b125faa6dbf3ebe56bc7e40ae05c23"}, ] [package.dependencies] @@ -1015,13 +1015,13 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "gotrue" -version = "2.4.4" +version = "2.5.4" description = "Python Client Library for Supabase Auth" optional = false python-versions = "<4.0,>=3.8" files = [ - {file = "gotrue-2.4.4-py3-none-any.whl", hash = "sha256:2eef9c962820b114d355cd0690ec6aaeb03374efe8c6c75d2265d34483e9a67e"}, - {file = "gotrue-2.4.4.tar.gz", hash = "sha256:ba4652e3adb39c341a3a4f6a93ebe56b54e25b0959c66d1b38fd40fe4d75bff5"}, + {file = "gotrue-2.5.4-py3-none-any.whl", hash = "sha256:6f45003bc73cdee612a2d0be79cffed39c91cc8ad43a7440c02c320c7ad03a8e"}, + {file = "gotrue-2.5.4.tar.gz", hash = "sha256:acf0644a2e5d1bd70f66452361bfea4ba9621a0354a13154a333671a4c751c53"}, ] [package.dependencies] @@ -1159,13 +1159,13 @@ tests = ["freezegun", "pytest", "pytest-cov"] [[package]] name = "identify" -version = "2.5.36" +version = "2.6.0" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"}, - {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"}, + {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, + {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, ] [package.extras] @@ -1195,22 +1195,22 @@ files = [ [[package]] name = "importlib-metadata" -version = "7.1.0" +version = "8.0.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, - {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, + {file = "importlib_metadata-8.0.0-py3-none-any.whl", hash = "sha256:15584cf2b1bf449d98ff8a6ff1abef57bf20f3ac6454f431736cd3e660921b2f"}, + {file = "importlib_metadata-8.0.0.tar.gz", hash = "sha256:188bd24e4c346d3f0a933f275c2fec67050326a856b9a359881d7c2a697e8812"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "importlib-resources" @@ -1463,18 +1463,17 @@ adal = ["adal (>=1.0.2)"] [[package]] name = "livereload" -version = "2.6.3" +version = "2.7.0" description = "Python LiveReload is an awesome tool for web developers" optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"}, - {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, + {file = "livereload-2.7.0-py3-none-any.whl", hash = "sha256:19bee55aff51d5ade6ede0dc709189a0f904d3b906d3ea71641ed548acff3246"}, + {file = "livereload-2.7.0.tar.gz", hash = "sha256:f4ba199ef93248902841e298670eebfe1aa9e148e19b343bc57dbf1b74de0513"}, ] [package.dependencies] -six = "*" -tornado = {version = "*", markers = "python_version > \"2.7\""} +tornado = "*" [[package]] name = "markdown" @@ -1647,6 +1646,17 @@ pillow = ">=6.2.0" pyparsing = ">=2.3.1" python-dateutil = ">=2.7" +[[package]] +name = "more-itertools" +version = "10.3.0" +description = "More routines for operating on iterables, beyond itertools" +optional = false +python-versions = ">=3.8" +files = [ + {file = "more-itertools-10.3.0.tar.gz", hash = "sha256:e5d93ef411224fbcef366a6e8ddc4c5781bc6359d43412a65dd5964e46111463"}, + {file = "more_itertools-10.3.0-py3-none-any.whl", hash = "sha256:ea6a02e24a9161e51faad17a8782b92a0df82c12c1c8886fec7f0c3fa1a1b320"}, +] + [[package]] name = "mypy" version = "0.991" @@ -1793,13 +1803,13 @@ urllib3 = ">=1.26.5" [[package]] name = "packaging" -version = "24.0" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] @@ -1839,8 +1849,8 @@ files = [ [package.dependencies] numpy = [ {version = ">=1.20.3", markers = "python_version < \"3.10\""}, - {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, {version = ">=1.21.0", markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -1882,84 +1892,95 @@ files = [ [[package]] name = "pillow" -version = "10.3.0" +version = "10.4.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.8" files = [ - {file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"}, - {file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"}, - {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"}, - {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"}, - {file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"}, - {file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"}, - {file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"}, - {file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"}, - {file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"}, - {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"}, - {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"}, - {file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"}, - {file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"}, - {file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"}, - {file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"}, - {file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"}, - {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"}, - {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"}, - {file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"}, - {file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"}, - {file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"}, - {file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"}, - {file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"}, - {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"}, - {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"}, - {file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"}, - {file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"}, - {file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"}, - {file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"}, - {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"}, - {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"}, - {file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"}, - {file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"}, - {file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"}, - {file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"}, -] - -[package.extras] -docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] + {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, + {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"}, + {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"}, + {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"}, + {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"}, + {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"}, + {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, + {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, + {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, + {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, + {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, + {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, + {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, + {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, + {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, + {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, + {file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"}, + {file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"}, + {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"}, + {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"}, + {file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"}, + {file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"}, + {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"}, + {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"}, + {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"}, + {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"}, + {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"}, + {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"}, + {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"}, + {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] fpx = ["olefile"] mic = ["olefile"] tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] @@ -2142,47 +2163,54 @@ files = [ [[package]] name = "pydantic" -version = "1.10.15" +version = "1.10.17" description = "Data validation and settings management using python type hints" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-1.10.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:22ed12ee588b1df028a2aa5d66f07bf8f8b4c8579c2e96d5a9c1f96b77f3bb55"}, - {file = "pydantic-1.10.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75279d3cac98186b6ebc2597b06bcbc7244744f6b0b44a23e4ef01e5683cc0d2"}, - {file = "pydantic-1.10.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50f1666a9940d3d68683c9d96e39640f709d7a72ff8702987dab1761036206bb"}, - {file = "pydantic-1.10.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82790d4753ee5d00739d6cb5cf56bceb186d9d6ce134aca3ba7befb1eedbc2c8"}, - {file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d207d5b87f6cbefbdb1198154292faee8017d7495a54ae58db06762004500d00"}, - {file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e49db944fad339b2ccb80128ffd3f8af076f9f287197a480bf1e4ca053a866f0"}, - {file = "pydantic-1.10.15-cp310-cp310-win_amd64.whl", hash = "sha256:d3b5c4cbd0c9cb61bbbb19ce335e1f8ab87a811f6d589ed52b0254cf585d709c"}, - {file = "pydantic-1.10.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c3d5731a120752248844676bf92f25a12f6e45425e63ce22e0849297a093b5b0"}, - {file = "pydantic-1.10.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c365ad9c394f9eeffcb30a82f4246c0006417f03a7c0f8315d6211f25f7cb654"}, - {file = "pydantic-1.10.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3287e1614393119c67bd4404f46e33ae3be3ed4cd10360b48d0a4459f420c6a3"}, - {file = "pydantic-1.10.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be51dd2c8596b25fe43c0a4a59c2bee4f18d88efb8031188f9e7ddc6b469cf44"}, - {file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6a51a1dd4aa7b3f1317f65493a182d3cff708385327c1c82c81e4a9d6d65b2e4"}, - {file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4e316e54b5775d1eb59187f9290aeb38acf620e10f7fd2f776d97bb788199e53"}, - {file = "pydantic-1.10.15-cp311-cp311-win_amd64.whl", hash = "sha256:0d142fa1b8f2f0ae11ddd5e3e317dcac060b951d605fda26ca9b234b92214986"}, - {file = "pydantic-1.10.15-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7ea210336b891f5ea334f8fc9f8f862b87acd5d4a0cbc9e3e208e7aa1775dabf"}, - {file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3453685ccd7140715e05f2193d64030101eaad26076fad4e246c1cc97e1bb30d"}, - {file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bea1f03b8d4e8e86702c918ccfd5d947ac268f0f0cc6ed71782e4b09353b26f"}, - {file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:005655cabc29081de8243126e036f2065bd7ea5b9dff95fde6d2c642d39755de"}, - {file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:af9850d98fc21e5bc24ea9e35dd80a29faf6462c608728a110c0a30b595e58b7"}, - {file = "pydantic-1.10.15-cp37-cp37m-win_amd64.whl", hash = "sha256:d31ee5b14a82c9afe2bd26aaa405293d4237d0591527d9129ce36e58f19f95c1"}, - {file = "pydantic-1.10.15-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5e09c19df304b8123938dc3c53d3d3be6ec74b9d7d0d80f4f4b5432ae16c2022"}, - {file = "pydantic-1.10.15-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7ac9237cd62947db00a0d16acf2f3e00d1ae9d3bd602b9c415f93e7a9fc10528"}, - {file = "pydantic-1.10.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:584f2d4c98ffec420e02305cf675857bae03c9d617fcfdc34946b1160213a948"}, - {file = "pydantic-1.10.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbc6989fad0c030bd70a0b6f626f98a862224bc2b1e36bfc531ea2facc0a340c"}, - {file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d573082c6ef99336f2cb5b667b781d2f776d4af311574fb53d908517ba523c22"}, - {file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6bd7030c9abc80134087d8b6e7aa957e43d35714daa116aced57269a445b8f7b"}, - {file = "pydantic-1.10.15-cp38-cp38-win_amd64.whl", hash = "sha256:3350f527bb04138f8aff932dc828f154847fbdc7a1a44c240fbfff1b57f49a12"}, - {file = "pydantic-1.10.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:51d405b42f1b86703555797270e4970a9f9bd7953f3990142e69d1037f9d9e51"}, - {file = "pydantic-1.10.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a980a77c52723b0dc56640ced396b73a024d4b74f02bcb2d21dbbac1debbe9d0"}, - {file = "pydantic-1.10.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67f1a1fb467d3f49e1708a3f632b11c69fccb4e748a325d5a491ddc7b5d22383"}, - {file = "pydantic-1.10.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:676ed48f2c5bbad835f1a8ed8a6d44c1cd5a21121116d2ac40bd1cd3619746ed"}, - {file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:92229f73400b80c13afcd050687f4d7e88de9234d74b27e6728aa689abcf58cc"}, - {file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2746189100c646682eff0bce95efa7d2e203420d8e1c613dc0c6b4c1d9c1fde4"}, - {file = "pydantic-1.10.15-cp39-cp39-win_amd64.whl", hash = "sha256:394f08750bd8eaad714718812e7fab615f873b3cdd0b9d84e76e51ef3b50b6b7"}, - {file = "pydantic-1.10.15-py3-none-any.whl", hash = "sha256:28e552a060ba2740d0d2aabe35162652c1459a0b9069fe0db7f4ee0e18e74d58"}, - {file = "pydantic-1.10.15.tar.gz", hash = "sha256:ca832e124eda231a60a041da4f013e3ff24949d94a01154b137fc2f2a43c3ffb"}, + {file = "pydantic-1.10.17-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fa51175313cc30097660b10eec8ca55ed08bfa07acbfe02f7a42f6c242e9a4b"}, + {file = "pydantic-1.10.17-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7e8988bb16988890c985bd2093df9dd731bfb9d5e0860db054c23034fab8f7a"}, + {file = "pydantic-1.10.17-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:371dcf1831f87c9e217e2b6a0c66842879a14873114ebb9d0861ab22e3b5bb1e"}, + {file = "pydantic-1.10.17-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4866a1579c0c3ca2c40575398a24d805d4db6cb353ee74df75ddeee3c657f9a7"}, + {file = "pydantic-1.10.17-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:543da3c6914795b37785703ffc74ba4d660418620cc273490d42c53949eeeca6"}, + {file = "pydantic-1.10.17-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7623b59876f49e61c2e283551cc3647616d2fbdc0b4d36d3d638aae8547ea681"}, + {file = "pydantic-1.10.17-cp310-cp310-win_amd64.whl", hash = "sha256:409b2b36d7d7d19cd8310b97a4ce6b1755ef8bd45b9a2ec5ec2b124db0a0d8f3"}, + {file = "pydantic-1.10.17-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fa43f362b46741df8f201bf3e7dff3569fa92069bcc7b4a740dea3602e27ab7a"}, + {file = "pydantic-1.10.17-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2a72d2a5ff86a3075ed81ca031eac86923d44bc5d42e719d585a8eb547bf0c9b"}, + {file = "pydantic-1.10.17-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4ad32aed3bf5eea5ca5decc3d1bbc3d0ec5d4fbcd72a03cdad849458decbc63"}, + {file = "pydantic-1.10.17-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeb4e741782e236ee7dc1fb11ad94dc56aabaf02d21df0e79e0c21fe07c95741"}, + {file = "pydantic-1.10.17-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d2f89a719411cb234105735a520b7c077158a81e0fe1cb05a79c01fc5eb59d3c"}, + {file = "pydantic-1.10.17-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db3b48d9283d80a314f7a682f7acae8422386de659fffaba454b77a083c3937d"}, + {file = "pydantic-1.10.17-cp311-cp311-win_amd64.whl", hash = "sha256:9c803a5113cfab7bbb912f75faa4fc1e4acff43e452c82560349fff64f852e1b"}, + {file = "pydantic-1.10.17-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:820ae12a390c9cbb26bb44913c87fa2ff431a029a785642c1ff11fed0a095fcb"}, + {file = "pydantic-1.10.17-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c1e51d1af306641b7d1574d6d3307eaa10a4991542ca324f0feb134fee259815"}, + {file = "pydantic-1.10.17-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e53fb834aae96e7b0dadd6e92c66e7dd9cdf08965340ed04c16813102a47fab"}, + {file = "pydantic-1.10.17-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e2495309b1266e81d259a570dd199916ff34f7f51f1b549a0d37a6d9b17b4dc"}, + {file = "pydantic-1.10.17-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:098ad8de840c92ea586bf8efd9e2e90c6339d33ab5c1cfbb85be66e4ecf8213f"}, + {file = "pydantic-1.10.17-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:525bbef620dac93c430d5d6bdbc91bdb5521698d434adf4434a7ef6ffd5c4b7f"}, + {file = "pydantic-1.10.17-cp312-cp312-win_amd64.whl", hash = "sha256:6654028d1144df451e1da69a670083c27117d493f16cf83da81e1e50edce72ad"}, + {file = "pydantic-1.10.17-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c87cedb4680d1614f1d59d13fea353faf3afd41ba5c906a266f3f2e8c245d655"}, + {file = "pydantic-1.10.17-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11289fa895bcbc8f18704efa1d8020bb9a86314da435348f59745473eb042e6b"}, + {file = "pydantic-1.10.17-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94833612d6fd18b57c359a127cbfd932d9150c1b72fea7c86ab58c2a77edd7c7"}, + {file = "pydantic-1.10.17-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d4ecb515fa7cb0e46e163ecd9d52f9147ba57bc3633dca0e586cdb7a232db9e3"}, + {file = "pydantic-1.10.17-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7017971ffa7fd7808146880aa41b266e06c1e6e12261768a28b8b41ba55c8076"}, + {file = "pydantic-1.10.17-cp37-cp37m-win_amd64.whl", hash = "sha256:e840e6b2026920fc3f250ea8ebfdedf6ea7a25b77bf04c6576178e681942ae0f"}, + {file = "pydantic-1.10.17-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bfbb18b616abc4df70591b8c1ff1b3eabd234ddcddb86b7cac82657ab9017e33"}, + {file = "pydantic-1.10.17-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebb249096d873593e014535ab07145498957091aa6ae92759a32d40cb9998e2e"}, + {file = "pydantic-1.10.17-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8c209af63ccd7b22fba94b9024e8b7fd07feffee0001efae50dd99316b27768"}, + {file = "pydantic-1.10.17-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4b40c9e13a0b61583e5599e7950490c700297b4a375b55b2b592774332798b7"}, + {file = "pydantic-1.10.17-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c31d281c7485223caf6474fc2b7cf21456289dbaa31401844069b77160cab9c7"}, + {file = "pydantic-1.10.17-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae5184e99a060a5c80010a2d53c99aee76a3b0ad683d493e5f0620b5d86eeb75"}, + {file = "pydantic-1.10.17-cp38-cp38-win_amd64.whl", hash = "sha256:ad1e33dc6b9787a6f0f3fd132859aa75626528b49cc1f9e429cdacb2608ad5f0"}, + {file = "pydantic-1.10.17-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e17c0ee7192e54a10943f245dc79e36d9fe282418ea05b886e1c666063a7b54"}, + {file = "pydantic-1.10.17-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cafb9c938f61d1b182dfc7d44a7021326547b7b9cf695db5b68ec7b590214773"}, + {file = "pydantic-1.10.17-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95ef534e3c22e5abbdbdd6f66b6ea9dac3ca3e34c5c632894f8625d13d084cbe"}, + {file = "pydantic-1.10.17-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d96b8799ae3d782df7ec9615cb59fc32c32e1ed6afa1b231b0595f6516e8ab"}, + {file = "pydantic-1.10.17-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ab2f976336808fd5d539fdc26eb51f9aafc1f4b638e212ef6b6f05e753c8011d"}, + {file = "pydantic-1.10.17-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8ad363330557beac73159acfbeed220d5f1bfcd6b930302a987a375e02f74fd"}, + {file = "pydantic-1.10.17-cp39-cp39-win_amd64.whl", hash = "sha256:48db882e48575ce4b39659558b2f9f37c25b8d348e37a2b4e32971dd5a7d6227"}, + {file = "pydantic-1.10.17-py3-none-any.whl", hash = "sha256:e41b5b973e5c64f674b3b4720286ded184dcc26a691dd55f34391c62c6934688"}, + {file = "pydantic-1.10.17.tar.gz", hash = "sha256:f434160fb14b353caf634149baaf847206406471ba70e64657c1e8330277a991"}, ] [package.dependencies] @@ -2245,20 +2273,19 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pyjwt" -version = "2.8.0" +version = "1.7.1" description = "JSON Web Token implementation in Python" optional = false -python-versions = ">=3.7" +python-versions = "*" files = [ - {file = "PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"}, - {file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"}, + {file = "PyJWT-1.7.1-py2.py3-none-any.whl", hash = "sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e"}, + {file = "PyJWT-1.7.1.tar.gz", hash = "sha256:8d59a976fb773f3e6a39c85636357c4f0e242707394cadadd9814f5cbaa20e96"}, ] [package.extras] -crypto = ["cryptography (>=3.4.0)"] -dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] -docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] -tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] +crypto = ["cryptography (>=1.4)"] +flake8 = ["flake8", "flake8-import-order", "pep8-naming"] +test = ["pytest (>=4.0.1,<5.0.0)", "pytest-cov (>=2.6.0,<3.0.0)", "pytest-runner (>=4.2,<5.0.0)"] [[package]] name = "pymsteams" @@ -2412,18 +2439,18 @@ files = [ [[package]] name = "realtime" -version = "1.0.5" +version = "1.0.6" description = "" optional = false python-versions = "<4.0,>=3.8" files = [ - {file = "realtime-1.0.5-py3-none-any.whl", hash = "sha256:93342fbcb8812ed8d81733f2782c1199376f0471e78014675420c7d31f2f327d"}, - {file = "realtime-1.0.5.tar.gz", hash = "sha256:4abbb3218b6ce8bd8d9d3b1112661d325e36ceab67a0e918673d0fd8fca04fb1"}, + {file = "realtime-1.0.6-py3-none-any.whl", hash = "sha256:c66918a106d8ef348d1821f2dbf6683d8833825580d95b2fdea9995406b42838"}, + {file = "realtime-1.0.6.tar.gz", hash = "sha256:2be0d8a6305513d423604ee319216108fc20105cb7438922d5c8958c48f40a47"}, ] [package.dependencies] python-dateutil = ">=2.8.1,<3.0.0" -typing-extensions = ">=4.11.0,<5.0.0" +typing-extensions = ">=4.12.2,<5.0.0" websockets = ">=11,<13" [[package]] @@ -2766,13 +2793,13 @@ files = [ [[package]] name = "slack-sdk" -version = "3.27.2" +version = "3.31.0" description = "The Slack API Platform SDK for Python" optional = false python-versions = ">=3.6" files = [ - {file = "slack_sdk-3.27.2-py2.py3-none-any.whl", hash = "sha256:af97158e6ac7f667e158e8036e63dc1f79db9bd36216a33c10fcc49be7c2f30c"}, - {file = "slack_sdk-3.27.2.tar.gz", hash = "sha256:bb145bf2bd93b60a17cd55c05cb15868c9a07d845b6fb608c798b50bce21cb99"}, + {file = "slack_sdk-3.31.0-py2.py3-none-any.whl", hash = "sha256:a120cc461e8ebb7d9175f171dbe0ded37a6878d9f7b96b28e4bad1227399047b"}, + {file = "slack_sdk-3.31.0.tar.gz", hash = "sha256:740d2f9c49cbfcbd46fca56b4be9d527934c225312aac18fd2c0fca0ef6bc935"}, ] [package.extras] @@ -2966,13 +2993,13 @@ resolved_reference = "93cd0f883707e6b755a9d1b330ec35517548cdd2" [[package]] name = "sphinx-reredirects" -version = "0.1.3" +version = "0.1.4" description = "Handles redirects for moved pages in Sphinx documentation projects" optional = false python-versions = ">=3.5" files = [ - {file = "sphinx_reredirects-0.1.3-py3-none-any.whl", hash = "sha256:02c53437c467cf9ed89863eff3addedc01d129624b2f03ab6302518fb77a2c89"}, - {file = "sphinx_reredirects-0.1.3.tar.gz", hash = "sha256:56e222d285f76c944fd370f36ad3a1a66103a88b552e97d3d24a622bb9465de8"}, + {file = "sphinx_reredirects-0.1.4-py3-none-any.whl", hash = "sha256:f27995e233a62336fc254330543d8b0f779ec83089da36274e27f5a5f2d5f529"}, + {file = "sphinx_reredirects-0.1.4.tar.gz", hash = "sha256:b8effdb31a069823cd8b5b029bfe1e04ac584615a0ebea35c5a8c8daf05a354b"}, ] [package.dependencies] @@ -3144,13 +3171,13 @@ test = ["pylint", "pytest", "pytest-black", "pytest-cov", "pytest-pylint"] [[package]] name = "supabase" -version = "2.5.0" +version = "2.5.1" description = "Supabase client for Python." optional = false python-versions = "<4.0,>=3.8" files = [ - {file = "supabase-2.5.0-py3-none-any.whl", hash = "sha256:13e5ed9e9377a1a69e70ad18ed7b82997cf13ffcd28173952f7503e4d5067771"}, - {file = "supabase-2.5.0.tar.gz", hash = "sha256:133dc832dfdd617f2f90ac5b52664df96ac8a9302ac6656ee769dc3f545812f0"}, + {file = "supabase-2.5.1-py3-none-any.whl", hash = "sha256:74a1f24f04fede1967ef084b50dea688228f7b10eb2f9d73350fe2251a865188"}, + {file = "supabase-2.5.1.tar.gz", hash = "sha256:c50e0eba5b03de3abd5ac0f887957ca43558ba44c4d17bb44e73ec454b41734c"}, ] [package.dependencies] @@ -3191,13 +3218,13 @@ widechars = ["wcwidth"] [[package]] name = "tenacity" -version = "8.3.0" +version = "8.5.0" description = "Retry code until it succeeds" optional = false python-versions = ">=3.8" files = [ - {file = "tenacity-8.3.0-py3-none-any.whl", hash = "sha256:3649f6443dbc0d9b01b9d8020a9c4ec7a1ff5f6f3c6c8a036ef371f573fe9185"}, - {file = "tenacity-8.3.0.tar.gz", hash = "sha256:953d4e6ad24357bceffbc9707bc74349aca9d245f68eb65419cf0c249a1949a2"}, + {file = "tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687"}, + {file = "tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78"}, ] [package.extras] @@ -3326,13 +3353,13 @@ types-urllib3 = "*" [[package]] name = "types-requests" -version = "2.32.0.20240602" +version = "2.32.0.20240622" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" files = [ - {file = "types-requests-2.32.0.20240602.tar.gz", hash = "sha256:3f98d7bbd0dd94ebd10ff43a7fbe20c3b8528acace6d8efafef0b6a184793f06"}, - {file = "types_requests-2.32.0.20240602-py3-none-any.whl", hash = "sha256:ed3946063ea9fbc6b5fc0c44fa279188bae42d582cb63760be6cb4b9d06c3de8"}, + {file = "types-requests-2.32.0.20240622.tar.gz", hash = "sha256:ed5e8a412fcc39159d6319385c009d642845f250c63902718f605cd90faade31"}, + {file = "types_requests-2.32.0.20240622-py3-none-any.whl", hash = "sha256:97bac6b54b5bd4cf91d407e62f0932a74821bc2211f22116d9ee1dd643826caf"}, ] [package.dependencies] @@ -3413,13 +3440,13 @@ devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3) [[package]] name = "urllib3" -version = "1.26.18" +version = "1.26.19" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ - {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, - {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, + {file = "urllib3-1.26.19-py2.py3-none-any.whl", hash = "sha256:37a0344459b199fce0e80b0d3569837ec6b6937435c5244e7fd73fa6006830f3"}, + {file = "urllib3-1.26.19.tar.gz", hash = "sha256:3e3d753a8618b86d7de333b4223005f68720bcd6a7d2bcb9fbd2229ec7c1e429"}, ] [package.extras] @@ -3446,13 +3473,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.26.2" +version = "20.26.3" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"}, - {file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"}, + {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, + {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, ] [package.dependencies] @@ -3528,18 +3555,17 @@ files = [ [[package]] name = "webexteamssdk" -version = "1.6.1" +version = "1.7" description = "Community-developed Python SDK for the Webex Teams APIs" optional = false python-versions = "*" files = [ - {file = "webexteamssdk-1.6.1-py3-none-any.whl", hash = "sha256:52a7f9d515cd3d53a853e679e16572ec6ca036a223e35b14fea14c99f492a6a4"}, - {file = "webexteamssdk-1.6.1.tar.gz", hash = "sha256:bbc7672f381b26fb22d0d03f87d131a2fa1e7d54c2f37f2e4cd28d725b8b5dfb"}, + {file = "webexteamssdk-1.7.tar.gz", hash = "sha256:4cc8e7d000d5cf3e25913966bfb2af6d023b1dcc51b75e1cea19e89ebc3f1d00"}, ] [package.dependencies] future = "*" -PyJWT = "*" +PyJWT = "1.7.1" requests = ">=2.4.2" requests-toolbelt = "*" @@ -3689,4 +3715,4 @@ all = ["CairoSVG", "Flask", "better-exceptions", "datadog-api-client", "grafana- [metadata] lock-version = "2.0" python-versions = "^3.8, <3.12" -content-hash = "a8d7793fd5676e36266a1d2fe0566e25c4a0b6795e0547c23e5e08be84fb09dd" +content-hash = "34fe45a868b57cab6892af478146f6dbf67ac94703274cc87af3ef201326a9bf" diff --git a/pyproject.toml b/pyproject.toml index 2e97a736b..5a0ffe959 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,6 +71,8 @@ boto3 = "1.28.72" prometheus-api-client = "0.5.4" requests = "^2.32.3" certifi = "^2023.7.22" +regex = "2024.5.15" +more_itertools = { version = "^10.3", python = "<3.12" } [tool.poetry.dev-dependencies] pre-commit = "^2.13.0" diff --git a/src/robusta/core/sinks/transformer.py b/src/robusta/core/sinks/transformer.py index e0d4cceec..36a3d3441 100644 --- a/src/robusta/core/sinks/transformer.py +++ b/src/robusta/core/sinks/transformer.py @@ -1,7 +1,6 @@ import logging import re import urllib.parse -from collections import defaultdict from typing import List, Optional, Union import markdown2 @@ -28,6 +27,7 @@ def tabulate(*args, **kwargs): ScanReportBlock, TableBlock, ) +from robusta.utils.trim_markdown import trim_markdown class Transformer: @@ -58,6 +58,13 @@ def apply_length_limit(msg: str, max_length: int, truncator: Optional[str] = Non truncator = truncator or "..." return msg[: max_length - len(truncator)] + truncator + @staticmethod + def apply_length_limit_to_markdown(msg: str, max_length: int, truncator: str = "...") -> str: + try: + return trim_markdown(msg, max_length, truncator) + except: + return Transformer.apply_length_limit(msg, max_length, truncator) + @staticmethod def to_markdown_diff(block: KubernetesDiffBlock, use_emoji_sign: bool = False) -> List[ListBlock]: # this can happen when a block.old=None or block.new=None - e.g. the resource was added or deleted diff --git a/src/robusta/integrations/slack/sender.py b/src/robusta/integrations/slack/sender.py index 52f953837..fe5f2a9f8 100644 --- a/src/robusta/integrations/slack/sender.py +++ b/src/robusta/integrations/slack/sender.py @@ -146,7 +146,7 @@ def __to_slack_markdown(self, block: MarkdownBlock) -> List[SlackBlock]: "type": "section", "text": { "type": "mrkdwn", - "text": Transformer.apply_length_limit(block.text, MAX_BLOCK_CHARS), + "text": Transformer.apply_length_limit_to_markdown(block.text, MAX_BLOCK_CHARS), }, } ] diff --git a/src/robusta/utils/trim_markdown.py b/src/robusta/utils/trim_markdown.py new file mode 100644 index 000000000..10418b454 --- /dev/null +++ b/src/robusta/utils/trim_markdown.py @@ -0,0 +1,57 @@ +try: + from itertools import batched +except ImportError: # Python < 3.12 + from more_itertools import batched + +import regex + + +def trim_markdown(text: str, max_length: int, suffix: str = "...") -> str: + # This method of trimming markdown is not universal. It only takes care of correctly + # trimming block sections. Implementing a general truncation method for markdown that + # would handle all the possible tags in a correct way would be rather complex. + + trim_idx = max_length - len(suffix) + + if trim_idx <= 0: # The pathological cases. + return suffix[:max_length] + + # Process block quotes backwards in the input + for match_open, match_close in batched(regex.finditer("```", text, regex.REVERSE), 2): + open_start, open_end = match_close.span() + close_start, close_end = match_open.span() + if trim_idx >= close_end: + # Trimming point after this block quote + return text[:trim_idx] + suffix + elif trim_idx < open_start: + # Trimming point before this block quote - continue to the preceding block + continue + elif trim_idx >= open_start and trim_idx < open_start + 3: + # Trimming point inside the opening block quote tag + return text[:trim_idx].rstrip("`") + suffix + elif trim_idx >= close_start and trim_idx < close_end: + # Trimming point inside the closing block quote tag + if trim_idx - open_end >= 3: # Enough space to insert the closing tag + return text[:trim_idx - 3] + "```" + suffix + else: # Not enough space, strip the whole block + return text[:open_start] + suffix + elif trim_idx >= open_end and trim_idx < close_start: + # Trimming point inside the block quote + if trim_idx - open_end >= 3: # Enough space to insert the closing tag + return text[:trim_idx - 3] + "```" + suffix + else: # Not enough space, strip the whole block + return text[:open_start] + suffix + else: + # This should never happen + raise Exception( + f'Internal error in trim_markdown, text="{text[:12]}"(...), {max_length=}, suffix="{suffix}", ' + f'matched code block {open_start}..{close_end}' + ) + + # Cases when there were no code blocks in the input + if len(text) <= trim_idx: + return text + elif len(text) < max_length: + return (text[:trim_idx] + suffix)[:max_length] + else: + return text[:trim_idx] + suffix diff --git a/tests/test_trim_markdown.py b/tests/test_trim_markdown.py new file mode 100644 index 000000000..c708e9c32 --- /dev/null +++ b/tests/test_trim_markdown.py @@ -0,0 +1,71 @@ +import pytest + +from robusta.utils.trim_markdown import trim_markdown + + +@pytest.mark.parametrize( + "max_length,expected_output", [ + (0, ""), + (1, "#"), + (2, "##"), + (3, "##"), + (4, "##"), + (5, "##"), + (6, "##"), + (7, "##"), + (8, "``````##"), + (9, "```o```##"), + (10, "```oh```##"), + (13, "```oh``` he##"), + (16, "```oh``` hello##"), + (17, "```oh``` hello ##"), + (18, "```oh``` hello ##"), + (19, "```oh``` hello ##"), + (20, "```oh``` hello ##"), + (21, "```oh``` hello ##"), + (22, "```oh``` hello ##"), + (23, "```oh``` hello ``````##"), + (24, "```oh``` hello ```w```##"), + (25, "```oh``` hello ```wo```##"), + (27, "```oh``` hello ```worl```##"), + (28, "```oh``` hello ```world```##"), + (29, "```oh``` hello ```world``` ##"), + (31, "```oh``` hello ```world``` an##"), + (39, "```oh``` hello ```world``` and then ##"), + (42, "```oh``` hello ```world``` and then ##"), + (44, "```oh``` hello ```world``` and then ``````##"), + (48, "```oh``` hello ```world``` and then ```some```##"), + (52, "```oh``` hello ```world``` and then ```somethin```##"), + (53, "```oh``` hello ```world``` and then ```something```##"), + (54, "```oh``` hello ```world``` and then ```something```##"), + (111, "```oh``` hello ```world``` and then ```something```##"), + ]) +def test_trim_markdown(max_length: int, expected_output: str): + text = "```oh``` hello ```world``` and then ```something```" + trimmed = trim_markdown(text, max_length, "##") + assert trimmed == expected_output + assert len(trimmed) <= max_length + + +@pytest.mark.parametrize( + "max_length,expected_output", [ + (0, ""), + (1, "$"), + (2, "$$"), + (3, "$$$"), + (4, "N$$$"), + (5, "No$$$"), + (10, "No code$$$"), + (38, "No code blocks whatsoever in this t$$$"), + (39, "No code blocks whatsoever in this te$$$"), + (40, "No code blocks whatsoever in this tex$$$"), + (41, "No code blocks whatsoever in this text"), + (42, "No code blocks whatsoever in this text"), + (111, "No code blocks whatsoever in this text"), + ] +) +def test_trim_markdown_no_code_blocks(max_length: int, expected_output: str): + text = "No code blocks whatsoever in this text" + trimmed = trim_markdown(text, max_length, "$$$") + assert trimmed == expected_output + assert len(trimmed) <= max_length From d1c94548701ab9b0cad5b2c69227ec036d9a5db3 Mon Sep 17 00:00:00 2001 From: Robert Szefler Date: Tue, 9 Jul 2024 10:58:51 +0200 Subject: [PATCH 6/8] Make header information in mails sent by the mail sink optional (#1491) --- docs/configuration/sinks/mail.rst | 5 +++++ src/robusta/core/sinks/mail/mail_sink.py | 2 +- src/robusta/core/sinks/mail/mail_sink_params.py | 3 +++ src/robusta/integrations/mail/sender.py | 12 +++++++----- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/configuration/sinks/mail.rst b/docs/configuration/sinks/mail.rst index 1136d2d81..678561a96 100644 --- a/docs/configuration/sinks/mail.rst +++ b/docs/configuration/sinks/mail.rst @@ -29,6 +29,11 @@ Configuring the mail sink - mail_sink: name: mail_sink mailto: "mailtos://user:password@server&from=a@x&to=b@y,c@z" + with_header: false # optional + +The default value of the optional `with_header` parameter is `true`. If set to `false`, mails +sent by this sink will *not* include header information, such as the finding header, investigate +button and the source of the notification. .. note:: diff --git a/src/robusta/core/sinks/mail/mail_sink.py b/src/robusta/core/sinks/mail/mail_sink.py index 356d1de3c..c61eb6f45 100644 --- a/src/robusta/core/sinks/mail/mail_sink.py +++ b/src/robusta/core/sinks/mail/mail_sink.py @@ -15,4 +15,4 @@ def __init__(self, sink_config: MailSinkConfigWrapper, registry): ) def write_finding(self, finding: Finding, platform_enabled: bool): - self.sender.send_finding(finding, platform_enabled) + self.sender.send_finding(finding, platform_enabled, self.params.with_header) diff --git a/src/robusta/core/sinks/mail/mail_sink_params.py b/src/robusta/core/sinks/mail/mail_sink_params.py index 07e5d7016..f64a2b294 100644 --- a/src/robusta/core/sinks/mail/mail_sink_params.py +++ b/src/robusta/core/sinks/mail/mail_sink_params.py @@ -1,3 +1,5 @@ +from typing import Optional + from pydantic import validator from robusta.core.sinks.sink_base_params import SinkBaseParams @@ -6,6 +8,7 @@ class MailSinkParams(SinkBaseParams): mailto: str + with_header: Optional[bool] = True @classmethod def _get_sink_type(cls): diff --git a/src/robusta/integrations/mail/sender.py b/src/robusta/integrations/mail/sender.py index b651dc096..0f42a94d3 100644 --- a/src/robusta/integrations/mail/sender.py +++ b/src/robusta/integrations/mail/sender.py @@ -22,18 +22,20 @@ def __init__(self, mailto: str, account_id: str, cluster_name: str, signing_key: self.account_id = account_id self.cluster_name = cluster_name - def send_finding(self, finding: Finding, platform_enabled: bool): + def send_finding(self, finding: Finding, platform_enabled: bool, include_headers: bool): blocks: List[BaseBlock] = [] status: FindingStatus = ( FindingStatus.RESOLVED if finding.title.startswith("[RESOLVED]") else FindingStatus.FIRING ) - blocks.append(self.__create_finding_header(finding, status)) - if platform_enabled: - blocks.append(self.create_links(finding, html_class="header_links")) + if include_headers: + blocks.append(self.__create_finding_header(finding, status)) + if platform_enabled: + blocks.append(self.create_links(finding, html_class="header_links")) + + blocks.append(MarkdownBlock(text=f"*Source:* `{self.cluster_name}`")) - blocks.append(MarkdownBlock(text=f"*Source:* `{self.cluster_name}`")) if finding.description: if finding.source == FindingSource.PROMETHEUS: blocks.append(MarkdownBlock(f"{Emojis.Alert.value} *Alert:* {finding.description}")) From 9e7adf63d0ecb65aff107f5a935200e80eb6ef23 Mon Sep 17 00:00:00 2001 From: Robert Szefler Date: Wed, 10 Jul 2024 06:57:42 +0200 Subject: [PATCH 7/8] Annotations and labels in event templated strings (#1492) --- playbooks/robusta_playbooks/common_actions.py | 6 ++++-- src/robusta/utils/parsing.py | 17 ++++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/playbooks/robusta_playbooks/common_actions.py b/playbooks/robusta_playbooks/common_actions.py index d08bad683..c17ec4579 100644 --- a/playbooks/robusta_playbooks/common_actions.py +++ b/playbooks/robusta_playbooks/common_actions.py @@ -43,7 +43,9 @@ def customise_finding(event: ExecutionBaseEvent, params: FindingOverrides): class FindingFields(ActionParams): """ - :var title: Finding title. Title can be templated with name/namespace/kind/node of the resource, if applicable + :var title: Finding title. Title can be templated with name/namespace/kind/node of the resource, if applicable. + | + Additionally, templating with labels and annotations of the resource is supported via $labels.label_name and $annotations.annotation_name. :var aggregation_key: Aggregation Keys are used for grouping similar types of notifications together. | For example, all CrashLoopBackOff notifications should have the same Aggregation Key so that Sinks can group them together. @@ -54,7 +56,7 @@ class FindingFields(ActionParams): :var description: Finding description. Description can be templated :var severity: Finding severity. Allowed values: DEBUG, INFO, LOW, MEDIUM, HIGH - :example title: "Job $name on namespace $namespace failed" + :example title: "Job $name (importance: $labels.importance) in namespace $namespace failed" :example aggregation_key: "JobFailure" :example severity: DEBUG """ diff --git a/src/robusta/utils/parsing.py b/src/robusta/utils/parsing.py index dea091412..e5c6006d2 100644 --- a/src/robusta/utils/parsing.py +++ b/src/robusta/utils/parsing.py @@ -10,13 +10,20 @@ from robusta.core.reporting import FindingSubject +class ExtendedTemplate(Template): + # Add the possibility of using the "." character in placeholders in order to support + # annotations and labels. Note the pattern is case-insensitive (see the documentation + # of string.Template). + idpattern = r'(?a:[_a-z][_a-z0-9.]*)' + + def format_event_templated_string(subject: Union[FindingSubject, ObjectReference], string_to_substitute) -> str: """ For templating strings based on event subjects """ - labels: Dict[str, str] = defaultdict(lambda: "") + variables: Dict[str, str] = defaultdict(lambda: "") kind = subject.kind if isinstance(subject, ObjectReference) else subject.subject_type.value - labels.update( + variables.update( { "name": subject.name, "kind": kind, @@ -24,7 +31,11 @@ def format_event_templated_string(subject: Union[FindingSubject, ObjectReference "node": subject.node if isinstance(subject, FindingSubject) and subject.node else "", } ) - return Template(string_to_substitute).safe_substitute(labels) + if isinstance(subject, FindingSubject): + variables.update({f"labels.{key}": value for key, value in subject.labels.items()}) + variables.update({f"annotations.{key}": value for key, value in subject.annotations.items()}) + return ExtendedTemplate(string_to_substitute).safe_substitute(variables) + def load_json(s: Union[str, bytes]) -> Any: """ From d49347d47a5e91e8eaf1cae31abda0337cbb3262 Mon Sep 17 00:00:00 2001 From: Natan Yellin Date: Wed, 10 Jul 2024 15:19:34 +0300 Subject: [PATCH 8/8] add note on slack auth (#1495) * Update ai-analysis.rst * Update slack.rst --- docs/configuration/sinks/slack.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/configuration/sinks/slack.rst b/docs/configuration/sinks/slack.rst index 100fa298e..a8e672418 100644 --- a/docs/configuration/sinks/slack.rst +++ b/docs/configuration/sinks/slack.rst @@ -20,13 +20,17 @@ Alternatively, generate a key by running ``robusta integrations slack`` and set # slack integration params - slack_sink: name: main_slack_sink - api_key: MY SLACK KEY + api_key: MY SLACK KEY # to avoid putting your key in Helm values, see below slack_channel: MY SLACK CHANNEL max_log_file_limit_kb: # (Default: 1000) The maximum allowed file size for "snippets" (in kilobytes) uploaded to the Slack channel. Larger files can be sent to Slack, but they may not be viewable directly within the Slack. channel_override: DYNAMIC SLACK CHANNEL OVERRIDE (Optional) Then do a :ref:`Helm Upgrade `. +.. warning:: + + If you don't want to put your Slack key in Helm values, you can use a secret. See the :ref:`Managing Secrets ` section for more information. + Notification Grouping ----------------------------- Slack allows grouping multiple notifications into summary messages and Slack threads. Refer to :ref:`Notification Grouping `.