From d53259aa58732f34c92dd48586d0693bdc8edfec Mon Sep 17 00:00:00 2001 From: Salar Nosrati-Ershad Date: Thu, 4 Aug 2022 19:40:42 +0430 Subject: [PATCH] Add MongoDB network data source name (DSN) schema (#3230) * Add MongoDsn to pydantic.networks with allowed_schemas and get_default_parts * Add unit test to MongoDsn and remove default host from default parts becouse it's required by MongoDB protocol * Fix import issues, follow contributing guide * Add changes to docs * Add changes to changelog --- changes/3229-snosratiershad.md | 1 + docs/usage/types.md | 7 +++++++ pydantic/__init__.py | 1 + pydantic/networks.py | 12 ++++++++++++ tests/test_networks.py | 28 ++++++++++++++++++++++++++++ 5 files changed, 49 insertions(+) create mode 100644 changes/3229-snosratiershad.md diff --git a/changes/3229-snosratiershad.md b/changes/3229-snosratiershad.md new file mode 100644 index 0000000..5eddf37 --- /dev/null +++ b/changes/3229-snosratiershad.md @@ -0,0 +1 @@ +Add MongoDB network data source name (DSN) schema diff --git a/docs/usage/types.md b/docs/usage/types.md index 02e9c43..15bb9f9 100644 --- a/docs/usage/types.md +++ b/docs/usage/types.md @@ -549,6 +549,12 @@ _(This script is complete, it should run "as is")_ `RedisDsn` : a redis DSN style URL; see [URLs](#urls) +`MongoDsn` +: a MongoDB DSN style URL; see [URLs](#urls) + +`KafkaDsn` +: a kafka DSN style URL; see [URLs](#urls) + `stricturl` : a type method for arbitrary URL constraints; see [URLs](#urls) @@ -644,6 +650,7 @@ For URI/URL validation the following types are available: - `postgresql+pygresql` - `AmqpDsn`: schema `amqp` or `amqps`, user info not required, TLD not required, host not required - `RedisDsn`: scheme `redis` or `rediss`, user info not required, tld not required, host not required (CHANGED: user info +- `MongoDsn` : scheme `mongodb`, user info not required, database name not required, port not required not required from **v1.6** onwards), user info may be passed without user part (e.g., `rediss://:pass@localhost`) - `stricturl`: method with the following keyword arguments: - `strip_whitespace: bool = True` diff --git a/pydantic/__init__.py b/pydantic/__init__.py index 7c79fd1..d4a5afa 100644 --- a/pydantic/__init__.py +++ b/pydantic/__init__.py @@ -59,6 +59,7 @@ __all__ = [ 'PostgresDsn', 'AmqpDsn', 'RedisDsn', + 'MongoDsn', 'KafkaDsn', 'validate_email', # parse diff --git a/pydantic/networks.py b/pydantic/networks.py index 7451e73..71396f8 100644 --- a/pydantic/networks.py +++ b/pydantic/networks.py @@ -74,6 +74,7 @@ __all__ = [ 'PostgresDsn', 'AmqpDsn', 'RedisDsn', + 'MongoDsn', 'KafkaDsn', 'validate_email', ] @@ -394,6 +395,17 @@ class RedisDsn(AnyUrl): } +class MongoDsn(AnyUrl): + allowed_schemes = {'mongodb'} + + # TODO: Needed to generic "Parts" for "Replica Set", "Sharded Cluster", and other mongodb deployment modes + @staticmethod + def get_default_parts(parts: 'Parts') -> 'Parts': + return { + 'port': '27017', + } + + class KafkaDsn(AnyUrl): allowed_schemes = {'kafka'} diff --git a/tests/test_networks.py b/tests/test_networks.py index 4d768b2..a1b1702 100644 --- a/tests/test_networks.py +++ b/tests/test_networks.py @@ -9,6 +9,7 @@ from pydantic import ( FileUrl, HttpUrl, KafkaDsn, + MongoDsn, NameEmail, PostgresDsn, RedisDsn, @@ -510,6 +511,33 @@ def test_redis_dsns(): assert m.a.path == '/0' +def test_mongodb_dsns(): + class Model(BaseModel): + a: MongoDsn + + # TODO: Need to unit tests about "Replica Set", "Sharded cluster" and other deployment modes of MongoDB + m = Model(a='mongodb://user:pass@localhost:1234/app') + assert m.a == 'mongodb://user:pass@localhost:1234/app' + assert m.a.user == 'user' + assert m.a.password == 'pass' + + with pytest.raises(ValidationError) as exc_info: + Model(a='http://example.org') + assert exc_info.value.errors()[0]['type'] == 'value_error.url.scheme' + + # Password is not required for MongoDB protocol + m = Model(a='mongodb://localhost:1234/app') + assert m.a == 'mongodb://localhost:1234/app' + assert m.a.user is None + assert m.a.password is None + + # Only schema and host is required for MongoDB protocol + m = Model(a='mongodb://localhost') + assert m.a.scheme == 'mongodb' + assert m.a.host == 'localhost' + assert m.a.port == '27017' + + def test_kafka_dsns(): class Model(BaseModel): a: KafkaDsn