Skip to content

Commit

Permalink
Validate format of tags to be max 255 characters and with certain cha…
Browse files Browse the repository at this point in the history
…racters (#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
  • Loading branch information
brandur authored Jul 6, 2024
1 parent e4947c9 commit 2f9b80f
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 5 deletions.
12 changes: 8 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- 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).

## [0.3.0] - 2024-07-04

### Added

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

## [0.2.0] - 2024-07-04

### Changed

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

## [0.1.2] - 2024-07-04

### Changed

- 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).
- 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).

## [0.1.1] - 2024-07-04

### Fixed

- Fix `pyproject.toml` description and add various URLs like to homepage, docs, and GitHub repositories. [PR #18](https://github.com/riverqueue/river/pull/18).
- 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).

## [0.1.0] - 2024-07-04

Expand Down
14 changes: 13 additions & 1 deletion src/riverqueue/client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from dataclasses import dataclass
from datetime import datetime, timezone, timedelta
import re
from typing import (
Any,
Awaitable,
Expand Down Expand Up @@ -320,7 +321,7 @@ def _make_insert_params(
queue=insert_opts.queue or args_insert_opts.queue or QUEUE_DEFAULT,
scheduled_at=scheduled_at and scheduled_at.astimezone(timezone.utc),
state="scheduled" if scheduled_at else "available",
tags=insert_opts.tags or args_insert_opts.tags or [],
tags=_validate_tags(insert_opts.tags or args_insert_opts.tags or []),
)

return insert_params, unique_opts
Expand Down Expand Up @@ -348,3 +349,14 @@ def _truncate_time(time, interval_seconds) -> datetime:
def _uint64_to_int64(uint64):
# Packs a uint64 then unpacks to int64 to fit within Postgres bigint
return (uint64 + (1 << 63)) % (1 << 64) - (1 << 63)


tag_re = re.compile("\A[\w][\w\-]+[\w]\Z")


def _validate_tags(tags: list[str]) -> list[str]:
for tag in tags:
assert (
len(tag) <= 255 and tag_re.match(tag)
), f"tags should be less than 255 characters in length and match regex {tag_re.pattern}"
return tags
20 changes: 20 additions & 0 deletions tests/client_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,26 @@ def to_json() -> None:
assert "args should return non-nil from `to_json`" == str(ex.value)


def test_tag_validation(client):
client.insert(
SimpleArgs(), insert_opts=InsertOpts(tags=["foo", "bar", "baz", "foo-bar-baz"])
)

with pytest.raises(AssertionError) as ex:
client.insert(SimpleArgs(), insert_opts=InsertOpts(tags=["commas,bad"]))
assert (
"tags should be less than 255 characters in length and match regex \A[\w][\w\-]+[\w]\Z"
== str(ex.value)
)

with pytest.raises(AssertionError) as ex:
client.insert(SimpleArgs(), insert_opts=InsertOpts(tags=["a" * 256]))
assert (
"tags should be less than 255 characters in length and match regex \A[\w][\w\-]+[\w]\Z"
== str(ex.value)
)


def test_check_advisory_lock_prefix_bounds():
Client(mock_driver, advisory_lock_prefix=123)

Expand Down

0 comments on commit 2f9b80f

Please sign in to comment.