Skip to content

Commit

Permalink
feat: implement Schema model
Browse files Browse the repository at this point in the history
  • Loading branch information
azmeuk committed May 24, 2024
1 parent c3b2f36 commit a505ad5
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 0 deletions.
10 changes: 10 additions & 0 deletions pydantic_scim2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
from .responses import PatchOperation
from .responses import PatchRequest
from .responses import SCIMError
from .schema import AttributeKind
from .schema import Mutability
from .schema import Returned
from .schema import Schema
from .schema import Uniqueness
from .service_provider import AuthenticationScheme
from .service_provider import AuthenticationSchemeKind
from .service_provider import Bulk
Expand Down Expand Up @@ -74,4 +79,9 @@
"Resource",
"Meta",
"ETag",
"Schema",
"AttributeKind",
"Mutability",
"Returned",
"Uniqueness",
]
103 changes: 103 additions & 0 deletions pydantic_scim2/schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
from enum import Enum
from typing import List
from typing import Optional

from pydantic import BaseModel

from .resource import Meta


class AttributeKind(Enum):
string = "string"
boolean = "boolean"
decimal = "decimal"
integer = "integer"
dateTime = "dateTime"
reference = "reference"
binary = "binary"
complex = "complex"


class Mutability(Enum):
readOnly = "readOnly"
readWrite = "readWrite"
immutable = "immutable"
writeOnly = "writeOnly"


class Returned(Enum):
always = "always"
never = "never"
default = "default"
request = "request"


class Uniqueness(Enum):
none = "none"
server = "server"
global_ = "global"


class Attribute(BaseModel):
name: str
"""The attribute's name."""

type: AttributeKind
"""The attribute's data type."""

subAttributes: Optional[List["Attribute"]] = None
"""When an attribute is of type "complex", "subAttributes" defines a set of
sub-attributes."""

multiValued: bool
"""A Boolean value indicating the attribute's plurality."""

description: str
"""The attribute's human-readable description."""

required: bool
"""A Boolean value that specifies whether or not the attribute is
required."""

canonicalValues: Optional[List[str]] = None
"""A collection of suggested canonical values that MAY be used (e.g.,
"work" and "home")."""

caseExact: bool = True
"""A Boolean value that specifies whether or not a string attribute is case
sensitive."""

mutability: Mutability = Mutability.readWrite
"""A single keyword indicating the circumstances under which the value of
the attribute can be (re)defined."""

returned: Returned = Returned.default
"""A single keyword that indicates when an attribute and associated values
are returned in response to a GET request or in response to a PUT, POST, or
PATCH request."""

uniqueness: Uniqueness = Uniqueness.none
"""A single keyword value that specifies how the service provider enforces
uniqueness of attribute values."""

referenceTypes: Optional[List[str]] = None
"""A multi-valued array of JSON strings that indicate the SCIM resource
types that may be referenced."""


class Schema(BaseModel):
id: str
"""The unique URI of the schema."""

name: Optional[str] = None
"""The schema's human-readable name."""

description: Optional[str] = None
"""The schema's human-readable description."""

attributes: List[Attribute]
"""A complex type that defines service provider attributes and their
qualities via the following set of sub-attributes."""

meta: Meta
"""A complex attribute containing resource metadata."""
73 changes: 73 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@
from pydantic import AnyUrl

from pydantic_scim2 import AddressKind
from pydantic_scim2 import AttributeKind
from pydantic_scim2 import AuthenticationSchemeKind
from pydantic_scim2 import EmailKind
from pydantic_scim2 import Group
from pydantic_scim2 import ImKind
from pydantic_scim2 import Mutability
from pydantic_scim2 import PhoneNumberKind
from pydantic_scim2 import PhotoKind
from pydantic_scim2 import ResourceType
from pydantic_scim2 import Returned
from pydantic_scim2 import Schema
from pydantic_scim2 import ServiceProviderConfiguration
from pydantic_scim2 import Uniqueness
from pydantic_scim2 import User


Expand Down Expand Up @@ -261,3 +266,71 @@ def test_resource_type(resource_type_payload):
assert obj.schema_ == AnyUrl("urn:ietf:params:scim:schemas:core:2.0:Group")
assert obj.meta.location == "https://example.com/v2/ResourceTypes/Group"
assert obj.meta.resourceType == "ResourceType"


def test_schema(resource_schema_payload):
obj = Schema.model_validate(resource_schema_payload[0])
obj = Schema.model_validate(resource_schema_payload[1])

assert obj.id == "urn:ietf:params:scim:schemas:core:2.0:Group"
assert obj.name == "Group"
assert obj.description == "Group"
assert obj.attributes[0].name == "displayName"
assert obj.attributes[0].type == AttributeKind.string
assert obj.attributes[0].multiValued is False
assert obj.attributes[0].description == (
"A human-readable name for the Group. " "REQUIRED."
)
assert obj.attributes[0].required is False
assert obj.attributes[0].caseExact is False
assert obj.attributes[0].mutability == Mutability.readWrite
assert obj.attributes[0].returned == Returned.default
assert obj.attributes[0].uniqueness == Uniqueness.none
assert obj.attributes[1].name == "members"
assert obj.attributes[1].type == AttributeKind.complex
assert obj.attributes[1].multiValued is True
assert obj.attributes[1].description == "A list of members of the Group."
assert obj.attributes[1].required is False
assert obj.attributes[1].subAttributes[0].name == "value"
assert obj.attributes[1].subAttributes[0].type == AttributeKind.string
assert obj.attributes[1].subAttributes[0].multiValued is False
assert (
obj.attributes[1].subAttributes[0].description
== "Identifier of the member of this Group."
)
assert obj.attributes[1].subAttributes[0].required is False
assert obj.attributes[1].subAttributes[0].caseExact is False
assert obj.attributes[1].subAttributes[0].mutability == Mutability.immutable
assert obj.attributes[1].subAttributes[0].returned == Returned.default
assert obj.attributes[1].subAttributes[0].uniqueness == Uniqueness.none
assert obj.attributes[1].subAttributes[1].name == "$ref"
assert obj.attributes[1].subAttributes[1].type == AttributeKind.reference
assert obj.attributes[1].subAttributes[1].referenceTypes == ["User", "Group"]
assert obj.attributes[1].subAttributes[1].multiValued is False
assert obj.attributes[1].subAttributes[1].description == (
"The URI corresponding to a SCIM resource " "that is a member of this Group."
)
assert obj.attributes[1].subAttributes[1].required is False
assert obj.attributes[1].subAttributes[1].caseExact is False
assert obj.attributes[1].subAttributes[1].mutability == Mutability.immutable
assert obj.attributes[1].subAttributes[1].returned == Returned.default
assert obj.attributes[1].subAttributes[1].uniqueness == Uniqueness.none
assert obj.attributes[1].subAttributes[2].name == "type"
assert obj.attributes[1].subAttributes[2].type == AttributeKind.string
assert obj.attributes[1].subAttributes[2].multiValued is False
assert obj.attributes[1].subAttributes[2].description == (
"A label indicating the type of resource, " "e.g., 'User' or 'Group'."
)
assert obj.attributes[1].subAttributes[2].required is False
assert obj.attributes[1].subAttributes[2].caseExact is False
assert obj.attributes[1].subAttributes[2].canonicalValues == ["User", "Group"]
assert obj.attributes[1].subAttributes[2].mutability == Mutability.immutable
assert obj.attributes[1].subAttributes[2].returned == Returned.default
assert obj.attributes[1].subAttributes[2].uniqueness == Uniqueness.none
assert obj.attributes[1].mutability == Mutability.readWrite
assert obj.attributes[1].returned == Returned.default
assert obj.meta.resourceType == "Schema"
assert (
obj.meta.location == "/v2/Schemas/urn:ietf:params:scim:schemas:core:2.0:Group"
)
obj = Schema.model_validate(resource_schema_payload[2])

0 comments on commit a505ad5

Please sign in to comment.