Skip to content

Commit

Permalink
Merge pull request #2 from ncstate-sat/LIBS-14-add-a-pydantic-models-…
Browse files Browse the repository at this point in the history
…module

Libs 14 add a pydantic models module
  • Loading branch information
JeremyGibson authored Jul 22, 2024
2 parents b14b40a + a334c9b commit 5858e69
Show file tree
Hide file tree
Showing 13 changed files with 429 additions and 96 deletions.
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)

0 comments on commit 5858e69

Please sign in to comment.