Skip to content

Commit 2f9b80f

Browse files
authored
Validate format of tags to be max 255 characters and with certain characters (#23)
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 2f9b80f

File tree

3 files changed

+41
-5
lines changed

3 files changed

+41
-5
lines changed

CHANGELOG.md

+8-4
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

+13-1
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

+20
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)