mirror of
https://github.com/checktheroads/hyperglass
synced 2024-05-11 05:55:08 +00:00
restructure location of models for cleaner importing/exporting
This commit is contained in:
14
.flake8
14
.flake8
@@ -7,19 +7,17 @@ exclude=.git, __pycache__, hyperglass/api/examples/*.py, hyperglass/compat/_ssht
|
|||||||
filename=*.py
|
filename=*.py
|
||||||
per-file-ignores=
|
per-file-ignores=
|
||||||
hyperglass/main.py:E402
|
hyperglass/main.py:E402
|
||||||
# Disable redefinition warning for exception handlers
|
|
||||||
hyperglass/api.py:F811
|
|
||||||
# Disable classmethod warning for validator decorators
|
# Disable classmethod warning for validator decorators
|
||||||
hyperglass/models.py:N805,E0213,R0903
|
hyperglass/models/*.py:N805,E0213,R0903,E501,C0301
|
||||||
# Disable unused import warning for modules
|
hyperglass/models/api/*.py:N805,E0213,R0903,E501,C0301
|
||||||
hyperglass/*/__init__.py:F401
|
hyperglass/models/commands/*.py:N805,E0213,R0903,E501,C0301
|
||||||
hyperglass/api/models/*.py:N805,E0213,R0903
|
|
||||||
hyperglass/api/models/response.py:E501,C0301
|
|
||||||
hyperglass/parsing/models/*.py:N805,E0213,R0903
|
hyperglass/parsing/models/*.py:N805,E0213,R0903
|
||||||
hyperglass/configuration/models/*.py:N805,E0213,R0903,E501,C0301
|
hyperglass/configuration/models/*.py:N805,E0213,R0903,E501,C0301
|
||||||
|
# Disable unused import warning for modules
|
||||||
|
hyperglass/*/__init__.py:F401
|
||||||
|
hyperglass/models/*/__init__.py:F401
|
||||||
ignore=W503,C0330,R504,D202,S403,S301
|
ignore=W503,C0330,R504,D202,S403,S301
|
||||||
select=B, BLK, C, D, E, F, I, II, N, P, PIE, S, R, W
|
select=B, BLK, C, D, E, F, I, II, N, P, PIE, S, R, W
|
||||||
disable-noqa=False
|
disable-noqa=False
|
||||||
hang-closing=False
|
hang-closing=False
|
||||||
max-complexity=10
|
max-complexity=10
|
||||||
# format=${cyan}%(path)s${reset}:${yellow_bold}%(row)d${reset}:${green_bold}%(col)d${reset}: ${red_bold}%(code)s${reset} %(text)s
|
|
||||||
|
@@ -36,7 +36,7 @@ from hyperglass.api.error_handlers import (
|
|||||||
default_handler,
|
default_handler,
|
||||||
validation_handler,
|
validation_handler,
|
||||||
)
|
)
|
||||||
from hyperglass.api.models.response import (
|
from hyperglass.models.api.response import (
|
||||||
QueryError,
|
QueryError,
|
||||||
InfoResponse,
|
InfoResponse,
|
||||||
QueryResponse,
|
QueryResponse,
|
||||||
|
@@ -1 +0,0 @@
|
|||||||
"""Query & Response Validation Models."""
|
|
@@ -19,10 +19,9 @@ from hyperglass.external import Webhook, bgptools
|
|||||||
from hyperglass.api.tasks import process_headers, import_public_key
|
from hyperglass.api.tasks import process_headers, import_public_key
|
||||||
from hyperglass.constants import __version__
|
from hyperglass.constants import __version__
|
||||||
from hyperglass.exceptions import HyperglassError
|
from hyperglass.exceptions import HyperglassError
|
||||||
|
from hyperglass.models.api import Query, EncodedRequest
|
||||||
from hyperglass.configuration import REDIS_CONFIG, params, devices
|
from hyperglass.configuration import REDIS_CONFIG, params, devices
|
||||||
from hyperglass.execution.main import execute
|
from hyperglass.execution.main import execute
|
||||||
from hyperglass.api.models.query import Query
|
|
||||||
from hyperglass.api.models.cert_import import EncodedRequest
|
|
||||||
|
|
||||||
APP_PATH = os.environ["hyperglass_directory"]
|
APP_PATH = os.environ["hyperglass_directory"]
|
||||||
|
|
||||||
|
@@ -28,6 +28,7 @@ from hyperglass.constants import (
|
|||||||
__version__,
|
__version__,
|
||||||
)
|
)
|
||||||
from hyperglass.exceptions import ConfigError, ConfigInvalid, ConfigMissing
|
from hyperglass.exceptions import ConfigError, ConfigInvalid, ConfigMissing
|
||||||
|
from hyperglass.models.commands import Commands
|
||||||
from hyperglass.configuration.defaults import (
|
from hyperglass.configuration.defaults import (
|
||||||
CREDIT,
|
CREDIT,
|
||||||
DEFAULT_HELP,
|
DEFAULT_HELP,
|
||||||
@@ -37,7 +38,6 @@ from hyperglass.configuration.defaults import (
|
|||||||
from hyperglass.configuration.markdown import get_markdown
|
from hyperglass.configuration.markdown import get_markdown
|
||||||
from hyperglass.configuration.models.params import Params
|
from hyperglass.configuration.models.params import Params
|
||||||
from hyperglass.configuration.models.devices import Devices
|
from hyperglass.configuration.models.devices import Devices
|
||||||
from hyperglass.configuration.models.commands import Commands
|
|
||||||
|
|
||||||
set_app_path(required=True)
|
set_app_path(required=True)
|
||||||
|
|
||||||
|
@@ -3,7 +3,8 @@
|
|||||||
from pydantic import Field, HttpUrl, StrictStr, StrictBool, constr
|
from pydantic import Field, HttpUrl, StrictStr, StrictBool, constr
|
||||||
|
|
||||||
# Project
|
# Project
|
||||||
from hyperglass.models import AnyUri, HyperglassModel
|
from hyperglass.models import HyperglassModel
|
||||||
|
from hyperglass.models.fields import AnyUri
|
||||||
|
|
||||||
DocsMode = constr(regex=r"(swagger|redoc)")
|
DocsMode = constr(regex=r"(swagger|redoc)")
|
||||||
|
|
||||||
|
@@ -16,7 +16,8 @@ from pydantic import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Project
|
# Project
|
||||||
from hyperglass.models import IntFloat, HyperglassModel
|
from hyperglass.models import HyperglassModel
|
||||||
|
from hyperglass.models.fields import IntFloat
|
||||||
from hyperglass.configuration.models.web import Web
|
from hyperglass.configuration.models.web import Web
|
||||||
from hyperglass.configuration.models.docs import Docs
|
from hyperglass.configuration.models.docs import Docs
|
||||||
from hyperglass.configuration.models.cache import Cache
|
from hyperglass.configuration.models.cache import Cache
|
||||||
|
5
hyperglass/external/__init__.py
vendored
5
hyperglass/external/__init__.py
vendored
@@ -1,5 +1,4 @@
|
|||||||
"""Functions & handlers for external data."""
|
"""Functions & handlers for external data."""
|
||||||
|
|
||||||
# Project
|
from .ripestat import RIPEStat # noqa: F401
|
||||||
from hyperglass.external.ripestat import RIPEStat # noqa: F401
|
from .webhooks import Webhook # noqa: F401
|
||||||
from hyperglass.external.webhooks import Webhook # noqa: F401
|
|
||||||
|
2
hyperglass/external/generic.py
vendored
2
hyperglass/external/generic.py
vendored
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
# Project
|
# Project
|
||||||
from hyperglass.log import log
|
from hyperglass.log import log
|
||||||
from hyperglass.models import Webhook
|
|
||||||
from hyperglass.external._base import BaseExternal
|
from hyperglass.external._base import BaseExternal
|
||||||
|
from hyperglass.models.webhook import Webhook
|
||||||
|
|
||||||
|
|
||||||
class GenericHook(BaseExternal, name="Generic"):
|
class GenericHook(BaseExternal, name="Generic"):
|
||||||
|
2
hyperglass/external/msteams.py
vendored
2
hyperglass/external/msteams.py
vendored
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
# Project
|
# Project
|
||||||
from hyperglass.log import log
|
from hyperglass.log import log
|
||||||
from hyperglass.models import Webhook
|
|
||||||
from hyperglass.external._base import BaseExternal
|
from hyperglass.external._base import BaseExternal
|
||||||
|
from hyperglass.models.webhook import Webhook
|
||||||
|
|
||||||
|
|
||||||
class MSTeams(BaseExternal, name="MSTeams"):
|
class MSTeams(BaseExternal, name="MSTeams"):
|
||||||
|
2
hyperglass/external/slack.py
vendored
2
hyperglass/external/slack.py
vendored
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
# Project
|
# Project
|
||||||
from hyperglass.log import log
|
from hyperglass.log import log
|
||||||
from hyperglass.models import Webhook
|
|
||||||
from hyperglass.external._base import BaseExternal
|
from hyperglass.external._base import BaseExternal
|
||||||
|
from hyperglass.models.webhook import Webhook
|
||||||
|
|
||||||
|
|
||||||
class SlackHook(BaseExternal, name="Slack"):
|
class SlackHook(BaseExternal, name="Slack"):
|
||||||
|
3
hyperglass/models/__init__.py
Normal file
3
hyperglass/models/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
"""All Data Models used by hyperglass."""
|
||||||
|
|
||||||
|
from .main import HyperglassModel, HyperglassModelExtra
|
11
hyperglass/models/api/__init__.py
Normal file
11
hyperglass/models/api/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
"""Query & Response Validation Models."""
|
||||||
|
from .query import Query
|
||||||
|
from .response import (
|
||||||
|
QueryError,
|
||||||
|
InfoResponse,
|
||||||
|
QueryResponse,
|
||||||
|
RoutersResponse,
|
||||||
|
CommunityResponse,
|
||||||
|
SupportedQueryResponse,
|
||||||
|
)
|
||||||
|
from .cert_import import EncodedRequest
|
@@ -6,7 +6,7 @@ from typing import Union
|
|||||||
from pydantic import BaseModel, StrictStr
|
from pydantic import BaseModel, StrictStr
|
||||||
|
|
||||||
# Project
|
# Project
|
||||||
from hyperglass.models import StrictBytes
|
from hyperglass.models.fields import StrictBytes
|
||||||
|
|
||||||
|
|
||||||
class EncodedRequest(BaseModel):
|
class EncodedRequest(BaseModel):
|
@@ -12,8 +12,9 @@ from pydantic import BaseModel, StrictStr, constr, validator
|
|||||||
# Project
|
# Project
|
||||||
from hyperglass.exceptions import InputInvalid
|
from hyperglass.exceptions import InputInvalid
|
||||||
from hyperglass.configuration import params, devices
|
from hyperglass.configuration import params, devices
|
||||||
from hyperglass.api.models.types import SupportedQuery
|
|
||||||
from hyperglass.api.models.validators import (
|
from .types import SupportedQuery
|
||||||
|
from .validators import (
|
||||||
validate_ip,
|
validate_ip,
|
||||||
validate_aspath,
|
validate_aspath,
|
||||||
validate_community_input,
|
validate_community_input,
|
@@ -1,15 +1,16 @@
|
|||||||
"""Validate command configuration variables."""
|
"""Validate command configuration variables."""
|
||||||
|
|
||||||
# Project
|
from .vyos import VyosCommands
|
||||||
from hyperglass.models import HyperglassModelExtra
|
from ..main import HyperglassModelExtra
|
||||||
from hyperglass.configuration.models.commands.vyos import VyosCommands
|
from .arista import AristaCommands
|
||||||
from hyperglass.configuration.models.commands.arista import AristaCommands
|
from .common import CommandGroup
|
||||||
from hyperglass.configuration.models.commands.common import CommandGroup
|
from .huawei import HuaweiCommands
|
||||||
from hyperglass.configuration.models.commands.huawei import HuaweiCommands
|
from .juniper import JuniperCommands
|
||||||
from hyperglass.configuration.models.commands.juniper import JuniperCommands
|
from .cisco_xr import CiscoXRCommands
|
||||||
from hyperglass.configuration.models.commands.cisco_xr import CiscoXRCommands
|
from .cisco_ios import CiscoIOSCommands
|
||||||
from hyperglass.configuration.models.commands.cisco_ios import CiscoIOSCommands
|
from .cisco_nxos import CiscoNXOSCommands
|
||||||
from hyperglass.configuration.models.commands.cisco_nxos import CiscoNXOSCommands
|
from .mikrotik_routeros import MikrotikRouterOS
|
||||||
|
from .mikrotik_switchos import MikrotikSwitchOS
|
||||||
|
|
||||||
_NOS_MAP = {
|
_NOS_MAP = {
|
||||||
"juniper": JuniperCommands,
|
"juniper": JuniperCommands,
|
||||||
@@ -18,6 +19,8 @@ _NOS_MAP = {
|
|||||||
"cisco_nxos": CiscoNXOSCommands,
|
"cisco_nxos": CiscoNXOSCommands,
|
||||||
"arista": AristaCommands,
|
"arista": AristaCommands,
|
||||||
"huawei": HuaweiCommands,
|
"huawei": HuaweiCommands,
|
||||||
|
"mikrotik_routeros": MikrotikRouterOS,
|
||||||
|
"mikrotik_switchos": MikrotikSwitchOS,
|
||||||
"vyos": VyosCommands,
|
"vyos": VyosCommands,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,6 +34,8 @@ class Commands(HyperglassModelExtra):
|
|||||||
cisco_xr: CommandGroup = CiscoXRCommands()
|
cisco_xr: CommandGroup = CiscoXRCommands()
|
||||||
cisco_nxos: CommandGroup = CiscoNXOSCommands()
|
cisco_nxos: CommandGroup = CiscoNXOSCommands()
|
||||||
huawei: CommandGroup = HuaweiCommands()
|
huawei: CommandGroup = HuaweiCommands()
|
||||||
|
mikrotik_routeros: CommandGroup = MikrotikRouterOS()
|
||||||
|
mikortik_switchos: CommandGroup = MikrotikSwitchOS()
|
||||||
vyos: CommandGroup = VyosCommands()
|
vyos: CommandGroup = VyosCommands()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
@@ -3,8 +3,7 @@
|
|||||||
# Third Party
|
# Third Party
|
||||||
from pydantic import StrictStr
|
from pydantic import StrictStr
|
||||||
|
|
||||||
# Project
|
from .common import CommandSet, CommandGroup
|
||||||
from hyperglass.configuration.models.commands.common import CommandSet, CommandGroup
|
|
||||||
|
|
||||||
|
|
||||||
class _IPv4(CommandSet):
|
class _IPv4(CommandSet):
|
@@ -3,8 +3,7 @@
|
|||||||
# Third Party
|
# Third Party
|
||||||
from pydantic import StrictStr
|
from pydantic import StrictStr
|
||||||
|
|
||||||
# Project
|
from .common import CommandSet, CommandGroup
|
||||||
from hyperglass.configuration.models.commands.common import CommandSet, CommandGroup
|
|
||||||
|
|
||||||
|
|
||||||
class _IPv4(CommandSet):
|
class _IPv4(CommandSet):
|
@@ -3,8 +3,7 @@
|
|||||||
# Third Party
|
# Third Party
|
||||||
from pydantic import StrictStr
|
from pydantic import StrictStr
|
||||||
|
|
||||||
# Project
|
from .common import CommandSet, CommandGroup
|
||||||
from hyperglass.configuration.models.commands.common import CommandSet, CommandGroup
|
|
||||||
|
|
||||||
|
|
||||||
class _IPv4(CommandSet):
|
class _IPv4(CommandSet):
|
@@ -3,8 +3,7 @@
|
|||||||
# Third Party
|
# Third Party
|
||||||
from pydantic import StrictStr
|
from pydantic import StrictStr
|
||||||
|
|
||||||
# Project
|
from .common import CommandSet, CommandGroup
|
||||||
from hyperglass.configuration.models.commands.common import CommandSet, CommandGroup
|
|
||||||
|
|
||||||
|
|
||||||
class _IPv4(CommandSet):
|
class _IPv4(CommandSet):
|
@@ -3,8 +3,7 @@
|
|||||||
# Third Party
|
# Third Party
|
||||||
from pydantic import StrictStr
|
from pydantic import StrictStr
|
||||||
|
|
||||||
# Project
|
from ..main import HyperglassModel, HyperglassModelExtra
|
||||||
from hyperglass.models import HyperglassModel, HyperglassModelExtra
|
|
||||||
|
|
||||||
|
|
||||||
class CommandSet(HyperglassModel):
|
class CommandSet(HyperglassModel):
|
@@ -3,8 +3,7 @@
|
|||||||
# Third Party
|
# Third Party
|
||||||
from pydantic import StrictStr
|
from pydantic import StrictStr
|
||||||
|
|
||||||
# Project
|
from .common import CommandSet, CommandGroup
|
||||||
from hyperglass.configuration.models.commands.common import CommandSet, CommandGroup
|
|
||||||
|
|
||||||
|
|
||||||
class _IPv4(CommandSet):
|
class _IPv4(CommandSet):
|
@@ -3,8 +3,7 @@
|
|||||||
# Third Party
|
# Third Party
|
||||||
from pydantic import StrictStr
|
from pydantic import StrictStr
|
||||||
|
|
||||||
# Project
|
from .common import CommandSet, CommandGroup
|
||||||
from hyperglass.configuration.models.commands.common import CommandSet, CommandGroup
|
|
||||||
|
|
||||||
|
|
||||||
class _IPv4(CommandSet):
|
class _IPv4(CommandSet):
|
@@ -3,8 +3,7 @@
|
|||||||
# Third Party
|
# Third Party
|
||||||
from pydantic import StrictStr
|
from pydantic import StrictStr
|
||||||
|
|
||||||
# Project
|
from .common import CommandSet, CommandGroup
|
||||||
from hyperglass.configuration.models.commands.common import CommandSet, CommandGroup
|
|
||||||
|
|
||||||
|
|
||||||
class _IPv4(CommandSet):
|
class _IPv4(CommandSet):
|
79
hyperglass/models/fields.py
Normal file
79
hyperglass/models/fields.py
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
"""Custom Pydantic Fields/Types."""
|
||||||
|
|
||||||
|
# Standard Library
|
||||||
|
import re
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
# Third Party
|
||||||
|
from pydantic import StrictInt, StrictFloat
|
||||||
|
|
||||||
|
IntFloat = TypeVar("IntFloat", StrictInt, StrictFloat)
|
||||||
|
|
||||||
|
|
||||||
|
class StrictBytes(bytes):
|
||||||
|
"""Custom data type for a strict byte string.
|
||||||
|
|
||||||
|
Used for validating the encoded JWT request payload.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __get_validators__(cls):
|
||||||
|
"""Yield Pydantic validator function.
|
||||||
|
|
||||||
|
See: https://pydantic-docs.helpmanual.io/usage/types/#custom-data-types
|
||||||
|
|
||||||
|
Yields:
|
||||||
|
{function} -- Validator
|
||||||
|
"""
|
||||||
|
yield cls.validate
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate(cls, value):
|
||||||
|
"""Validate type.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
value {Any} -- Pre-validated input
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
TypeError: Raised if value is not bytes
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
{object} -- Instantiated class
|
||||||
|
"""
|
||||||
|
if not isinstance(value, bytes):
|
||||||
|
raise TypeError("bytes required")
|
||||||
|
return cls()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""Return representation of object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
{str} -- Representation
|
||||||
|
"""
|
||||||
|
return f"StrictBytes({super().__repr__()})"
|
||||||
|
|
||||||
|
|
||||||
|
class AnyUri(str):
|
||||||
|
"""Custom field type for HTTP URI, e.g. /example."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __get_validators__(cls):
|
||||||
|
"""Pydantic custim field method."""
|
||||||
|
yield cls.validate
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate(cls, value):
|
||||||
|
"""Ensure URI string contains a leading forward-slash."""
|
||||||
|
uri_regex = re.compile(r"^(\/.*)$")
|
||||||
|
if not isinstance(value, str):
|
||||||
|
raise TypeError("AnyUri type must be a string")
|
||||||
|
match = uri_regex.fullmatch(value)
|
||||||
|
if not match:
|
||||||
|
raise ValueError(
|
||||||
|
"Invalid format. A URI must begin with a forward slash, e.g. '/example'"
|
||||||
|
)
|
||||||
|
return cls(match.group())
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""Stringify custom field representation."""
|
||||||
|
return f"AnyUri({super().__repr__()})"
|
99
hyperglass/models/main.py
Normal file
99
hyperglass/models/main.py
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
"""Data models used throughout hyperglass."""
|
||||||
|
|
||||||
|
# Standard Library
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Third Party
|
||||||
|
from pydantic import HttpUrl, BaseModel
|
||||||
|
|
||||||
|
_WEBHOOK_TITLE = "hyperglass received a valid query with the following data"
|
||||||
|
_ICON_URL = "https://res.cloudinary.com/hyperglass/image/upload/v1593192484/icon.png"
|
||||||
|
|
||||||
|
|
||||||
|
def clean_name(_name: str) -> str:
|
||||||
|
"""Remove unsupported characters from field names.
|
||||||
|
|
||||||
|
Converts any "desirable" seperators to underscore, then removes all
|
||||||
|
characters that are unsupported in Python class variable names.
|
||||||
|
Also removes leading numbers underscores.
|
||||||
|
"""
|
||||||
|
_replaced = re.sub(r"[\-|\.|\@|\~|\:\/|\s]", "_", _name)
|
||||||
|
_scrubbed = "".join(re.findall(r"([a-zA-Z]\w+|\_+)", _replaced))
|
||||||
|
return _scrubbed.lower()
|
||||||
|
|
||||||
|
|
||||||
|
class HyperglassModel(BaseModel):
|
||||||
|
"""Base model for all hyperglass configuration models."""
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
"""Default Pydantic configuration.
|
||||||
|
|
||||||
|
See https://pydantic-docs.helpmanual.io/usage/model_config
|
||||||
|
"""
|
||||||
|
|
||||||
|
validate_all = True
|
||||||
|
extra = "forbid"
|
||||||
|
validate_assignment = True
|
||||||
|
alias_generator = clean_name
|
||||||
|
json_encoders = {HttpUrl: lambda v: str(v)}
|
||||||
|
|
||||||
|
def export_json(self, *args, **kwargs):
|
||||||
|
"""Return instance as JSON.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
{str} -- Stringified JSON.
|
||||||
|
"""
|
||||||
|
|
||||||
|
export_kwargs = {
|
||||||
|
"by_alias": True,
|
||||||
|
"exclude_unset": False,
|
||||||
|
**kwargs,
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.json(*args, **export_kwargs)
|
||||||
|
|
||||||
|
def export_dict(self, *args, **kwargs):
|
||||||
|
"""Return instance as dictionary.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
{dict} -- Python dictionary.
|
||||||
|
"""
|
||||||
|
export_kwargs = {
|
||||||
|
"by_alias": True,
|
||||||
|
"exclude_unset": False,
|
||||||
|
**kwargs,
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.dict(*args, **export_kwargs)
|
||||||
|
|
||||||
|
def export_yaml(self, *args, **kwargs):
|
||||||
|
"""Return instance as YAML.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
{str} -- Stringified YAML.
|
||||||
|
"""
|
||||||
|
# Standard Library
|
||||||
|
import json
|
||||||
|
|
||||||
|
# Third Party
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
export_kwargs = {
|
||||||
|
"by_alias": kwargs.pop("by_alias", True),
|
||||||
|
"exclude_unset": kwargs.pop("by_alias", False),
|
||||||
|
}
|
||||||
|
|
||||||
|
return yaml.safe_dump(
|
||||||
|
json.loads(self.export_json(**export_kwargs)), *args, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class HyperglassModelExtra(HyperglassModel):
|
||||||
|
"""Model for hyperglass configuration models with dynamic fields."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
"""Default pydantic configuration."""
|
||||||
|
|
||||||
|
extra = "allow"
|
@@ -1,187 +1,21 @@
|
|||||||
"""Data models used throughout hyperglass."""
|
"""Data models used throughout hyperglass."""
|
||||||
|
|
||||||
# Standard Library
|
# Standard Library
|
||||||
import re
|
from typing import Optional
|
||||||
from typing import TypeVar, Optional
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
# Third Party
|
# Third Party
|
||||||
from pydantic import (
|
from pydantic import StrictStr, root_validator
|
||||||
HttpUrl,
|
|
||||||
BaseModel,
|
|
||||||
StrictInt,
|
|
||||||
StrictStr,
|
|
||||||
StrictFloat,
|
|
||||||
root_validator,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Project
|
# Project
|
||||||
from hyperglass.log import log
|
from hyperglass.log import log
|
||||||
|
|
||||||
IntFloat = TypeVar("IntFloat", StrictInt, StrictFloat)
|
from .main import HyperglassModel, HyperglassModelExtra
|
||||||
|
|
||||||
_WEBHOOK_TITLE = "hyperglass received a valid query with the following data"
|
_WEBHOOK_TITLE = "hyperglass received a valid query with the following data"
|
||||||
_ICON_URL = "https://res.cloudinary.com/hyperglass/image/upload/v1593192484/icon.png"
|
_ICON_URL = "https://res.cloudinary.com/hyperglass/image/upload/v1593192484/icon.png"
|
||||||
|
|
||||||
|
|
||||||
def clean_name(_name: str) -> str:
|
|
||||||
"""Remove unsupported characters from field names.
|
|
||||||
|
|
||||||
Converts any "desirable" seperators to underscore, then removes all
|
|
||||||
characters that are unsupported in Python class variable names.
|
|
||||||
Also removes leading numbers underscores.
|
|
||||||
"""
|
|
||||||
_replaced = re.sub(r"[\-|\.|\@|\~|\:\/|\s]", "_", _name)
|
|
||||||
_scrubbed = "".join(re.findall(r"([a-zA-Z]\w+|\_+)", _replaced))
|
|
||||||
return _scrubbed.lower()
|
|
||||||
|
|
||||||
|
|
||||||
class HyperglassModel(BaseModel):
|
|
||||||
"""Base model for all hyperglass configuration models."""
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
"""Default Pydantic configuration.
|
|
||||||
|
|
||||||
See https://pydantic-docs.helpmanual.io/usage/model_config
|
|
||||||
"""
|
|
||||||
|
|
||||||
validate_all = True
|
|
||||||
extra = "forbid"
|
|
||||||
validate_assignment = True
|
|
||||||
alias_generator = clean_name
|
|
||||||
json_encoders = {HttpUrl: lambda v: str(v)}
|
|
||||||
|
|
||||||
def export_json(self, *args, **kwargs):
|
|
||||||
"""Return instance as JSON.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
{str} -- Stringified JSON.
|
|
||||||
"""
|
|
||||||
|
|
||||||
export_kwargs = {
|
|
||||||
"by_alias": True,
|
|
||||||
"exclude_unset": False,
|
|
||||||
**kwargs,
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.json(*args, **export_kwargs)
|
|
||||||
|
|
||||||
def export_dict(self, *args, **kwargs):
|
|
||||||
"""Return instance as dictionary.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
{dict} -- Python dictionary.
|
|
||||||
"""
|
|
||||||
export_kwargs = {
|
|
||||||
"by_alias": True,
|
|
||||||
"exclude_unset": False,
|
|
||||||
**kwargs,
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.dict(*args, **export_kwargs)
|
|
||||||
|
|
||||||
def export_yaml(self, *args, **kwargs):
|
|
||||||
"""Return instance as YAML.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
{str} -- Stringified YAML.
|
|
||||||
"""
|
|
||||||
# Standard Library
|
|
||||||
import json
|
|
||||||
|
|
||||||
# Third Party
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
export_kwargs = {
|
|
||||||
"by_alias": kwargs.pop("by_alias", True),
|
|
||||||
"exclude_unset": kwargs.pop("by_alias", False),
|
|
||||||
}
|
|
||||||
|
|
||||||
return yaml.safe_dump(
|
|
||||||
json.loads(self.export_json(**export_kwargs)), *args, **kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class HyperglassModelExtra(HyperglassModel):
|
|
||||||
"""Model for hyperglass configuration models with dynamic fields."""
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
"""Default pydantic configuration."""
|
|
||||||
|
|
||||||
extra = "allow"
|
|
||||||
|
|
||||||
|
|
||||||
class AnyUri(str):
|
|
||||||
"""Custom field type for HTTP URI, e.g. /example."""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def __get_validators__(cls):
|
|
||||||
"""Pydantic custim field method."""
|
|
||||||
yield cls.validate
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def validate(cls, value):
|
|
||||||
"""Ensure URI string contains a leading forward-slash."""
|
|
||||||
uri_regex = re.compile(r"^(\/.*)$")
|
|
||||||
if not isinstance(value, str):
|
|
||||||
raise TypeError("AnyUri type must be a string")
|
|
||||||
match = uri_regex.fullmatch(value)
|
|
||||||
if not match:
|
|
||||||
raise ValueError(
|
|
||||||
"Invalid format. A URI must begin with a forward slash, e.g. '/example'"
|
|
||||||
)
|
|
||||||
return cls(match.group())
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
"""Stringify custom field representation."""
|
|
||||||
return f"AnyUri({super().__repr__()})"
|
|
||||||
|
|
||||||
|
|
||||||
class StrictBytes(bytes):
|
|
||||||
"""Custom data type for a strict byte string.
|
|
||||||
|
|
||||||
Used for validating the encoded JWT request payload.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def __get_validators__(cls):
|
|
||||||
"""Yield Pydantic validator function.
|
|
||||||
|
|
||||||
See: https://pydantic-docs.helpmanual.io/usage/types/#custom-data-types
|
|
||||||
|
|
||||||
Yields:
|
|
||||||
{function} -- Validator
|
|
||||||
"""
|
|
||||||
yield cls.validate
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def validate(cls, value):
|
|
||||||
"""Validate type.
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
value {Any} -- Pre-validated input
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
TypeError: Raised if value is not bytes
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
{object} -- Instantiated class
|
|
||||||
"""
|
|
||||||
if not isinstance(value, bytes):
|
|
||||||
raise TypeError("bytes required")
|
|
||||||
return cls()
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
"""Return representation of object.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
{str} -- Representation
|
|
||||||
"""
|
|
||||||
return f"StrictBytes({super().__repr__()})"
|
|
||||||
|
|
||||||
|
|
||||||
class WebhookHeaders(HyperglassModel):
|
class WebhookHeaders(HyperglassModel):
|
||||||
"""Webhook data model."""
|
"""Webhook data model."""
|
||||||
|
|
Reference in New Issue
Block a user