mirror of
https://github.com/rommapp/romm.git
synced 2026-10-11 22:40:04 +00:00
chore(deps): upgrade apprise to 2.0 (#5102)
This commit is contained in:
@@ -118,7 +118,7 @@ _HOST: Final = re.compile(r"\[[0-9a-f:.]+\]|[\w.-]+", re.I)
|
||||
# A token that is a URL path, which Apprise wants between slashes.
|
||||
_PATH_TOKEN: Final = "path"
|
||||
|
||||
FieldType = Literal["string", "int", "float", "bool", "choice", "list"]
|
||||
FieldType = Literal["string", "email", "int", "float", "bool", "choice", "list"]
|
||||
FieldValue = str | int | float | bool | list[str]
|
||||
|
||||
# What Apprise logs about the send running in this context: its warnings, and
|
||||
@@ -420,7 +420,8 @@ def _segment(field: AppriseField, value: FieldValue) -> str:
|
||||
return f"/{quote(path)}/" if path else "/"
|
||||
if isinstance(value, list):
|
||||
return field.delimiter.join(quote(str(item), safe="") for item in value)
|
||||
return quote(str(value), safe="")
|
||||
# Apprise reads an address's @ as the URL's own.
|
||||
return quote(str(value), safe="@" if field.type == "email" else "")
|
||||
|
||||
|
||||
def _fill(
|
||||
@@ -569,7 +570,9 @@ def _token_pattern(service: AppriseService, key: str) -> str:
|
||||
if key in _TOKEN_PATTERNS:
|
||||
return _TOKEN_PATTERNS[key]
|
||||
field = service.field(key)
|
||||
return r".+" if field is not None and field.type == "list" else r"[^/@?#]+"
|
||||
if field is not None and field.type == "list":
|
||||
return r".+"
|
||||
return r"[^/?#]+" if field is not None and field.type == "email" else r"[^/@?#]+"
|
||||
|
||||
|
||||
def _template_pattern(service: AppriseService, template: str) -> re.Pattern[str]:
|
||||
|
||||
@@ -26,6 +26,7 @@ from .fixtures import make_notification
|
||||
|
||||
_DISCORD = {"webhook_id": "1234567890", "webhook_token": "abcdefghijklmnop"}
|
||||
_JSON = {"host": "hooks.example.com", "path": "romm"}
|
||||
_SENDGRID = {"apikey": "abc123", "from_email": "[email protected]"}
|
||||
|
||||
|
||||
def _message(**overrides: Any) -> OutboundMessage:
|
||||
@@ -70,6 +71,16 @@ class TestCatalog:
|
||||
assert targets is not None
|
||||
assert (targets.type, targets.required) == ("list", True)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"service,key",
|
||||
[("sendgrid", "from_email"), ("ses", "from_email"), ("resend", "from_addr")],
|
||||
)
|
||||
def test_offers_an_email_address(self, service, key):
|
||||
field = find_service(service).field(key)
|
||||
|
||||
assert field is not None
|
||||
assert (field.type, field.required) == ("email", True)
|
||||
|
||||
def test_offers_the_schema_only_when_there_is_a_choice(self):
|
||||
schema = find_service("ntfy").field("schema")
|
||||
|
||||
@@ -154,6 +165,21 @@ class TestBuildUrl:
|
||||
{"host": "hooks.example.com", "path": "/api/romm"},
|
||||
"jsons://hooks.example.com/api/romm",
|
||||
),
|
||||
(
|
||||
"sendgrid",
|
||||
{**_SENDGRID, "targets": ["[email protected]"]},
|
||||
"sendgrid://abc123:[email protected]/me%40example.com",
|
||||
),
|
||||
(
|
||||
"ses",
|
||||
{
|
||||
"from_email": "[email protected]",
|
||||
"access_key_id": "AKID",
|
||||
"secret_access_key": "s3cr3t",
|
||||
"region": "us-east-1",
|
||||
},
|
||||
"ses://[email protected]/AKID/s3cr3t/us-east-1",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_fills_in_the_template_the_fields_make_up(self, service, fields, url):
|
||||
@@ -261,6 +287,11 @@ class TestFieldsFromUrl:
|
||||
"json",
|
||||
{"host": "hooks.example.com", "path": "api/romm"},
|
||||
),
|
||||
(
|
||||
"sendgrid://abc123:[email protected]/me%40example.com",
|
||||
"sendgrid",
|
||||
{**_SENDGRID, "targets": ["[email protected]"]},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_reads_a_url_back_into_its_service_and_fields(self, url, service, fields):
|
||||
@@ -269,6 +300,51 @@ class TestFieldsFromUrl:
|
||||
assert (found.id, read) == (service, fields)
|
||||
checked_fields(service, read)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"service,fields",
|
||||
[
|
||||
("sendgrid", {**_SENDGRID, "targets": ["[email protected]"]}),
|
||||
("brevo", _SENDGRID),
|
||||
("postmark", _SENDGRID),
|
||||
("mailersend", _SENDGRID),
|
||||
("resend", {"apikey": "abc123", "from_addr": "[email protected]"}),
|
||||
(
|
||||
"ses",
|
||||
{
|
||||
"from_email": "[email protected]",
|
||||
"access_key_id": "AKID",
|
||||
"secret_access_key": "s3cr3t",
|
||||
"region": "us-east-1",
|
||||
},
|
||||
),
|
||||
(
|
||||
"octopush",
|
||||
{
|
||||
"api_login": "[email protected]",
|
||||
"api_key": "k3y",
|
||||
"targets": ["+15551234567"],
|
||||
},
|
||||
),
|
||||
(
|
||||
"twist",
|
||||
{"password": "pw", "email": "[email protected]", "targets": ["#romm"]},
|
||||
),
|
||||
(
|
||||
"voipms",
|
||||
{
|
||||
"password": "pw",
|
||||
"email": "[email protected]",
|
||||
"from_phone": "15551234567",
|
||||
"targets": ["15557654321"],
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_reads_back_the_url_an_email_field_makes(self, service, fields):
|
||||
found, read = fields_from_url(build_url(find_service(service), fields))
|
||||
|
||||
assert (found.id, read) == (service, checked_fields(service, fields))
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"url,reason",
|
||||
[
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
export type AppriseFieldSchema = {
|
||||
key: string;
|
||||
label: string;
|
||||
type: 'string' | 'int' | 'float' | 'bool' | 'choice' | 'list';
|
||||
type: 'string' | 'email' | 'int' | 'float' | 'bool' | 'choice' | 'list';
|
||||
required: boolean;
|
||||
private: boolean;
|
||||
advanced: boolean;
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
NOTIFICATION_CHANNEL_URL_MAX_LENGTH,
|
||||
appriseFieldLabel,
|
||||
} from "@/v2/utils/notificationChannels";
|
||||
import { notBlank } from "@/v2/utils/validation";
|
||||
import { email, notBlank } from "@/v2/utils/validation";
|
||||
|
||||
const props = defineProps<{
|
||||
field: AppriseFieldSchema;
|
||||
@@ -32,6 +32,11 @@ const hint = computed(() =>
|
||||
const isNumber = computed(
|
||||
() => props.field.type === "int" || props.field.type === "float",
|
||||
);
|
||||
const inputType = computed(() => {
|
||||
if (props.field.private) return "password";
|
||||
if (isNumber.value) return "number";
|
||||
return props.field.type === "email" ? "email" : "text";
|
||||
});
|
||||
|
||||
function inRange(text: string): true | string {
|
||||
const { min, max, type } = props.field;
|
||||
@@ -54,6 +59,9 @@ function inRange(text: string): true | string {
|
||||
const rules = computed(() => [
|
||||
...(required.value ? [notBlank()] : []),
|
||||
...(isNumber.value ? [inRange] : []),
|
||||
...(props.field.type === "email"
|
||||
? [(text: string) => text === "" || email(text)]
|
||||
: []),
|
||||
]);
|
||||
const listRules = computed(() =>
|
||||
required.value
|
||||
@@ -97,7 +105,7 @@ const listRules = computed(() =>
|
||||
v-else
|
||||
:model-value="String(value)"
|
||||
:label="label"
|
||||
:type="field.private ? 'password' : isNumber ? 'number' : 'text'"
|
||||
:type="inputType"
|
||||
:rules="rules"
|
||||
:required="required"
|
||||
:hint="hint"
|
||||
|
||||
@@ -178,6 +178,52 @@ describe("NotificationChannelDialog", () => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it("takes an address in an email field and checks it", async () => {
|
||||
const sendgrid = makeAppriseService({
|
||||
id: "sendgrid",
|
||||
name: "SendGrid",
|
||||
fields: [
|
||||
{
|
||||
...makeAppriseService().fields[3]!,
|
||||
key: "apikey",
|
||||
label: "API Key",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
...makeAppriseService().fields[1]!,
|
||||
key: "from_email",
|
||||
label: "From Email",
|
||||
type: "email",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
api.getAppriseServices.mockResolvedValue({ data: [sendgrid] });
|
||||
api.create.mockResolvedValue({ data: channel({ type: "apprise" }) });
|
||||
const wrapper = await open(null, { admin: true });
|
||||
|
||||
await pick(wrapper, "apprise:sendgrid");
|
||||
await wrapper.findAll("input.r-text-field__input")[0]!.setValue("Mail");
|
||||
await input(wrapper, '[type="password"]').setValue("abc123");
|
||||
const address = input(wrapper, '[type="email"]');
|
||||
await address.setValue("romm");
|
||||
await save(wrapper);
|
||||
|
||||
expect(api.create).not.toHaveBeenCalled();
|
||||
expect(wrapper.text()).toContain("Invalid email");
|
||||
|
||||
await address.setValue("[email protected]");
|
||||
await save(wrapper);
|
||||
|
||||
expect(api.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
service: "sendgrid",
|
||||
fields: { apikey: "abc123", from_email: "[email protected]" },
|
||||
}),
|
||||
);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
const ntfyChannel = () =>
|
||||
channel({
|
||||
type: "apprise",
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ dependencies = [
|
||||
"asyncssh ~= 2.24",
|
||||
"alembic ~= 1.16",
|
||||
"anyio ~= 4.4",
|
||||
"apprise ~= 1.13",
|
||||
"apprise ~= 2.0",
|
||||
"authlib ~= 1.8.0",
|
||||
"bcrypt ~= 5.0",
|
||||
"certifi >= 2024.7.4",
|
||||
|
||||
@@ -154,7 +154,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "apprise"
|
||||
version = "1.13.1"
|
||||
version = "2.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
@@ -164,10 +164,11 @@ dependencies = [
|
||||
{ name = "requests" },
|
||||
{ name = "requests-oauthlib" },
|
||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/81/44/5965c245998c72022297e2b515d9594281a97ed1442b3fa022c2c2324102/apprise-1.13.1.tar.gz", hash = "sha256:e7689dda71aaf739244d6c8690de13cb1361b8d0a79980fb48bb397455ca0bdd", size = 2520230, upload-time = "2026-08-31T02:12:01.472Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/38/b0/2f2e9b6d9e52f7530b47ba7a9259ebf2378e3953f445222f54412fb27a68/apprise-2.0.0.tar.gz", hash = "sha256:aeb321737f951860d7cb0a9574159090cbdbcb0f1ba01c6b49c69018380adf8d", size = 3268123, upload-time = "2026-09-26T18:49:55.113Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/45/7d/6852bd3fc2eb48fa432cb22f5df11d1c902137dcecab93e0d8319b8c8e16/apprise-1.13.1-py3-none-any.whl", hash = "sha256:2ded6d00562dd2ae9eb6f1e6cc8d3fb66e06f7ede6efac0ad029031b633780f4", size = 1826431, upload-time = "2026-08-31T02:11:58.263Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/4e/9d3daa5cd16142b4077fbc3270b146ad965b8f81ffb4b538f781c4a1c858/apprise-2.0.0-py3-none-any.whl", hash = "sha256:945a2500a846b88b3c126a8d6e7c0472f6e3d94d8be52c836d8819f8db50b3d7", size = 2417371, upload-time = "2026-09-26T18:49:52.758Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2492,7 +2493,7 @@ requires-dist = [
|
||||
{ name = "aiohttp", specifier = "~=3.14" },
|
||||
{ name = "alembic", specifier = "~=1.16" },
|
||||
{ name = "anyio", specifier = "~=4.4" },
|
||||
{ name = "apprise", specifier = "~=1.13" },
|
||||
{ name = "apprise", specifier = "~=2.0" },
|
||||
{ name = "asyncssh", specifier = "~=2.24" },
|
||||
{ name = "authlib", specifier = "~=1.8.0" },
|
||||
{ name = "bcrypt", specifier = "~=5.0" },
|
||||
|
||||
Reference in New Issue
Block a user