Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Libs 14 add a pydantic models module #2

Merged
merged 11 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[run]
source = sat
omit = */tests/*, sat/ldap.py
omit = */tests/*
295 changes: 204 additions & 91 deletions notebooks/test_gravity_forms.ipynb

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "flit_core.buildapi"

[project]
name = "sat-utils"
version = "1.4.3"
version = "1.5.0"
authors = [
{ name="Ryan Semmler", email="[email protected]" },
{ name="Shawn Taylor", email="[email protected]" },
Expand All @@ -21,7 +21,9 @@ dependencies = [
"cx_Oracle==8.3.0",
"oracledb==2.0.1",
"pyodbc==5.1.0",
"requests_oauthlib==1.3.1"
"requests_oauthlib==1.3.1",
"pydantic==2.6.4",
"pydantic[email]==2.6.4"
]

[project.optional-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion pytest.ini
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[pytest]
testpaths = tests
python_files = tests.py test_*.py *_tests.py
addopts = -p no:warnings --cov-config=.coveragerc --cov-fail-under=85 --cov=sat --cov-report=html --cov-report=term-missing:skip-covered -vvv
addopts = -p no:warnings --cov-config=.coveragerc --cov-fail-under=86 --cov=sat --cov-report=html --cov-report=term-missing:skip-covered -vvv
18 changes: 17 additions & 1 deletion requirements/base/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#
# pip-compile --output-file=requirements/base/base.txt pyproject.toml
#
annotated-types==0.6.0
# via pydantic
certifi==2024.2.2
# via requests
cffi==1.16.0
Expand All @@ -14,14 +16,24 @@ cryptography==42.0.5
# via oracledb
cx-oracle==8.3.0
# via sat-utils (pyproject.toml)
dnspython==2.6.1
# via email-validator
email-validator==2.1.1
# via pydantic
idna==3.6
# via requests
# via
# email-validator
# requests
oauthlib==3.2.2
# via requests-oauthlib
oracledb==2.0.1
# via sat-utils (pyproject.toml)
pycparser==2.21
# via cffi
pydantic[email]==2.6.4
# via sat-utils (pyproject.toml)
pydantic-core==2.16.3
# via pydantic
pyodbc==5.1.0
# via sat-utils (pyproject.toml)
requests==2.31.0
Expand All @@ -32,5 +44,9 @@ requests-oauthlib==1.3.1
# via sat-utils (pyproject.toml)
slack-sdk==3.27.1
# via sat-utils (pyproject.toml)
typing-extensions==4.10.0
# via
# pydantic
# pydantic-core
urllib3==2.2.1
# via requests
13 changes: 13 additions & 0 deletions requirements/dev/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ aiofiles==22.1.0
# via ypy-websocket
aiosqlite==0.20.0
# via ypy-websocket
annotated-types==0.6.0
# via pydantic
anyio==4.3.0
# via jupyter-server
argon2-cffi==23.1.0
Expand Down Expand Up @@ -72,8 +74,12 @@ defusedxml==0.7.1
# via nbconvert
distlib==0.3.8
# via virtualenv
dnspython==2.6.1
# via email-validator
docutils==0.20.1
# via flit
email-validator==2.1.1
# via pydantic
entrypoints==0.4
# via jupyter-client
exceptiongroup==1.2.0
Expand All @@ -99,6 +105,7 @@ identify==2.5.35
idna==3.6
# via
# anyio
# email-validator
# jsonschema
# requests
iniconfig==2.0.0
Expand Down Expand Up @@ -310,6 +317,10 @@ py==1.11.0
# via pytest
pycparser==2.21
# via cffi
pydantic[email]==2.6.4
# via sat-utils (pyproject.toml)
pydantic-core==2.16.3
# via pydantic
pygments==2.17.2
# via
# ipython
Expand Down Expand Up @@ -461,6 +472,8 @@ typing-extensions==4.10.0
# anyio
# black
# mypy
# pydantic
# pydantic-core
uri-template==1.3.0
# via jsonschema
urllib3==2.2.1
Expand Down
Empty file added sat/models/__init__.py
Empty file.
Empty file added sat/models/ccure/__init__.py
Empty file.
14 changes: 14 additions & 0 deletions sat/models/ccure/access.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from pydantic import UUID4, BaseModel

from sat.models.ccure.types import FILLED_STRING


class Clearance(BaseModel):
object_id: int
guid: UUID4
name: FILLED_STRING


class Credential(BaseModel):
card_number: int
patron_id: int
10 changes: 10 additions & 0 deletions sat/models/ccure/assets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from pydantic import UUID4, BaseModel

from sat.models.ccure.types import ASSET_TYPES, FILLED_STRING


class Asset(BaseModel):
object_id: int
name: FILLED_STRING
guid: UUID4
asset_type: ASSET_TYPES
14 changes: 14 additions & 0 deletions sat/models/ccure/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from enum import Enum

from pydantic import AfterValidator
from typing_extensions import Annotated


def validate_filled_string(value):
if not value:
raise ValueError("This string may not be empty")


ASSET_TYPES = Enum("ASSET_TYPES", ["Door", "Elevator"])

FILLED_STRING = Annotated[str, AfterValidator(validate_filled_string)]
52 changes: 52 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from uuid import uuid4

import pytest
from sat.models.ccure.types import ASSET_TYPES


@pytest.fixture
def clearance():
def _clearance(multiple: int = None):
clearance = {
"name": "John Doe",
"object_id": 1234,
"guid": str(uuid4()),
}
if multiple:
return [clearance for _ in range(multiple)]
else:
return clearance

return _clearance


@pytest.fixture
def credential():
def _credential(multiple: int = None):
credential = {
"card_number": 1234567890,
"patron_id": 1234567890,
}
if multiple:
return [credential for _ in range(multiple)]
else:
return credential

return _credential


@pytest.fixture
def asset():
def _asset(multiple: int = None):
asset = {
"name": "Asset Name",
"object_id": 1234,
"guid": str(uuid4()),
"asset_type": ASSET_TYPES.Door,
}
if multiple:
return [asset for _ in range(multiple)]
else:
return asset

return _asset
99 changes: 99 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import pydantic
import pytest
from sat.models.ccure.access import Clearance, Credential
from sat.models.ccure.assets import Asset


def test_valid_clearance(clearance):
clr = clearance()
assert Clearance(**clr)


def test_invalid_clearance_uuid(clearance):
clr = clearance()
clr["guid"] = "asldkfj-aslkdjf-aldlas-asldkj"
with pytest.raises(pydantic.ValidationError) as ve:
Clearance(**clr)
assert "Input should be a valid UUID" in str(ve)


def test_invalid_clearance_name(clearance):
clr = clearance()
clr["name"] = ""
with pytest.raises(pydantic.ValidationError) as ve:
Clearance(**clr)
assert "This string may not be empty" in str(ve)


def test_invalid_clearance_name_non_string(clearance):
clr = clearance()
clr["name"] = 12
with pytest.raises(pydantic.ValidationError) as ve:
Clearance(**clr)
assert "Input should be a valid string" in str(ve)


def test_invalid_clearance_object_id(clearance):
clr = clearance()
clr["object_id"] = "1234b"
with pytest.raises(pydantic.ValidationError) as ve:
Clearance(**clr)
assert "Input should be a valid integer" in str(ve)


def test_valid_credential(credential):
cred = credential()
assert Credential(**cred)


def test_invalid_credential_card_number(credential):
cred = credential()
cred["card_number"] = "1234567890b"
with pytest.raises(pydantic.ValidationError) as ve:
Credential(**cred)
assert "Input should be a valid integer" in str(ve)


def test_invalid_credential_patron_id(credential):
cred = credential()
cred["patron_id"] = "1234567890b"
with pytest.raises(pydantic.ValidationError) as ve:
Credential(**cred)
assert "Input should be a valid integer" in str(ve)


def test_asset_valid(asset):
ast = asset()
assert Asset(**ast)


def test_asset_invalid_name(asset):
ast = asset()
ast["name"] = ""
with pytest.raises(pydantic.ValidationError) as ve:
Asset(**ast)
assert "This string may not be empty" in str(ve)


def test_asset_invalid_object_id(asset):
ast = asset()
ast["object_id"] = "1234b"
with pytest.raises(pydantic.ValidationError) as ve:
Asset(**ast)
assert "Input should be a valid integer" in str(ve)


def test_asset_invalid_guid(asset):
ast = asset()
ast["guid"] = "asldkfj-aslkdjf-aldlas-asldkj"
with pytest.raises(pydantic.ValidationError) as ve:
Asset(**ast)
assert "Input should be a valid UUID" in str(ve)


def test_asset_invalid_asset_type(asset):
ast = asset()
ast["asset_type"] = "Door"
with pytest.raises(pydantic.ValidationError) as ve:
Asset(**ast)
assert "Input should be 1 or 2" in str(ve)
Loading