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

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 committed Jul 6, 2024
1 parent e4947c9 commit febb2ba
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 febb2ba

Please sign in to comment.