Skip to content

Commit febb2ba

Browse files
committed
Validate format of tags to be max 255 characters and with certain characters
Match up tag validations that came in with the main project here [1]. The multi insertion queries use commas to split tags during batch inserts, so it's important that incoming tags don't have comms of their own. [1] riverqueue/river#351
1 parent e4947c9 commit febb2ba

File tree

3 files changed

+41
-5
lines changed

3 files changed

+41
-5
lines changed

CHANGELOG.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
12+
- Tags are now limited to 255 characters in length, and should match the regex `\A[\w][\w\-]+[\w]\z` (importantly, they can't contain commas). [PR #23](https://github.com/riverqueue/riverqueue-python/pull/23).
13+
1014
## [0.3.0] - 2024-07-04
1115

1216
### Added
1317

14-
- Implement `insert_many` and `insert_many_tx`. [PR #22](https://github.com/riverqueue/river/pull/22).
18+
- Implement `insert_many` and `insert_many_tx`. [PR #22](https://github.com/riverqueue/riverqueue-python/pull/22).
1519

1620
## [0.2.0] - 2024-07-04
1721

1822
### Changed
1923

20-
- Rename `Args` to `JobArgs` and add `JobArgsWithInsertOpts` protocol. [PR #20](https://github.com/riverqueue/river/pull/20).
24+
- Rename `Args` to `JobArgs` and add `JobArgsWithInsertOpts` protocol. [PR #20](https://github.com/riverqueue/riverqueue-python/pull/20).
2125

2226
## [0.1.2] - 2024-07-04
2327

2428
### Changed
2529

26-
- Add usage instructions README, add job state constants, and change return value of `insert_many()` and `insert_many_tx()` to an integer instead of a list of jobs. [PR #19](https://github.com/riverqueue/river/pull/19).
30+
- Add usage instructions README, add job state constants, and change return value of `insert_many()` and `insert_many_tx()` to an integer instead of a list of jobs. [PR #19](https://github.com/riverqueue/riverqueue-python/pull/19).
2731

2832
## [0.1.1] - 2024-07-04
2933

3034
### Fixed
3135

32-
- Fix `pyproject.toml` description and add various URLs like to homepage, docs, and GitHub repositories. [PR #18](https://github.com/riverqueue/river/pull/18).
36+
- Fix `pyproject.toml` description and add various URLs like to homepage, docs, and GitHub repositories. [PR #18](https://github.com/riverqueue/riverqueue-python/pull/18).
3337

3438
## [0.1.0] - 2024-07-04
3539

src/riverqueue/client.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from dataclasses import dataclass
22
from datetime import datetime, timezone, timedelta
3+
import re
34
from typing import (
45
Any,
56
Awaitable,
@@ -320,7 +321,7 @@ def _make_insert_params(
320321
queue=insert_opts.queue or args_insert_opts.queue or QUEUE_DEFAULT,
321322
scheduled_at=scheduled_at and scheduled_at.astimezone(timezone.utc),
322323
state="scheduled" if scheduled_at else "available",
323-
tags=insert_opts.tags or args_insert_opts.tags or [],
324+
tags=_validate_tags(insert_opts.tags or args_insert_opts.tags or []),
324325
)
325326

326327
return insert_params, unique_opts
@@ -348,3 +349,14 @@ def _truncate_time(time, interval_seconds) -> datetime:
348349
def _uint64_to_int64(uint64):
349350
# Packs a uint64 then unpacks to int64 to fit within Postgres bigint
350351
return (uint64 + (1 << 63)) % (1 << 64) - (1 << 63)
352+
353+
354+
tag_re = re.compile("\A[\w][\w\-]+[\w]\Z")
355+
356+
357+
def _validate_tags(tags: list[str]) -> list[str]:
358+
for tag in tags:
359+
assert (
360+
len(tag) <= 255 and tag_re.match(tag)
361+
), f"tags should be less than 255 characters in length and match regex {tag_re.pattern}"
362+
return tags

tests/client_test.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,26 @@ def to_json() -> None:
259259
assert "args should return non-nil from `to_json`" == str(ex.value)
260260

261261

262+
def test_tag_validation(client):
263+
client.insert(
264+
SimpleArgs(), insert_opts=InsertOpts(tags=["foo", "bar", "baz", "foo-bar-baz"])
265+
)
266+
267+
with pytest.raises(AssertionError) as ex:
268+
client.insert(SimpleArgs(), insert_opts=InsertOpts(tags=["commas,bad"]))
269+
assert (
270+
"tags should be less than 255 characters in length and match regex \A[\w][\w\-]+[\w]\Z"
271+
== str(ex.value)
272+
)
273+
274+
with pytest.raises(AssertionError) as ex:
275+
client.insert(SimpleArgs(), insert_opts=InsertOpts(tags=["a" * 256]))
276+
assert (
277+
"tags should be less than 255 characters in length and match regex \A[\w][\w\-]+[\w]\Z"
278+
== str(ex.value)
279+
)
280+
281+
262282
def test_check_advisory_lock_prefix_bounds():
263283
Client(mock_driver, advisory_lock_prefix=123)
264284

0 commit comments

Comments
 (0)