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

Group Attributes of Class and Object elements of Google Wallet, to make it more maintainable. #6

Open
wants to merge 38 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
5c23cd4
Group Attributes of Class and Object elements of Google Wallet, to ma…
loechel Apr 22, 2024
0f80398
move attributes around
loechel Apr 22, 2024
1e7671a
move attributes around
loechel Apr 22, 2024
9b76185
rename file notification.py to message.py as it only contains message…
loechel Apr 22, 2024
f4d137b
fix mypy issues with __call_ order on multiple inheritance
loechel Apr 23, 2024
246a3e4
move attributes around
loechel Apr 23, 2024
89bb64c
start implementing transit tickets
loechel Apr 23, 2024
a8feb00
make credential_file depended on a path
loechel Apr 23, 2024
7a327e5
make credential_file depended on a path
loechel Apr 23, 2024
0c3e063
move attributes around
loechel Apr 23, 2024
ed7ac8e
more Classes and Objects, Types and Enums added and cleaned-up
loechel Apr 23, 2024
f18c7a0
fix spelling in tox.ini
loechel Apr 23, 2024
b55ee19
more Classes and Objects, Types and Enums added and cleaned-up
loechel Apr 24, 2024
10f99e3
rename some classes to reflect Mixin character of those classes
loechel Apr 25, 2024
8f73896
More documentation and revert change from model_dump to model_dump_js…
loechel Apr 26, 2024
6802b85
cleanup models
loechel Apr 27, 2024
a9d3047
fix some classes
loechel Apr 28, 2024
1c96007
add debug statement
loechel Apr 28, 2024
63864c7
Update src/edutap/wallet_google/api.py
loechel Apr 29, 2024
dcaacbf
Update src/edutap/wallet_google/session.py
loechel Apr 29, 2024
74d977c
add pydantic-settings and refactor session to use this.
loechel Apr 29, 2024
7e6a7db
refactore settings
loechel Apr 30, 2024
5e36e14
extend Settings
loechel Apr 30, 2024
174135e
test fixtures
loechel Apr 30, 2024
950d60b
test fixtures
loechel Apr 30, 2024
ec7222d
Merge branch 'main' into group_attributes
loechel Apr 30, 2024
100b961
tox env handling
loechel Apr 30, 2024
8b6b094
tox env handling, changed to default env vars
loechel Apr 30, 2024
752dd79
fix enum equals method
loechel Apr 30, 2024
773d230
env var check
loechel Apr 30, 2024
665773f
try to fix tests
May 1, 2024
a2c80dc
try to fix settings for CI
loechel May 1, 2024
1942abf
fix all tox tests
loechel May 1, 2024
32f77dd
add kind attribute for retail classes
loechel May 2, 2024
5bd87d3
fix classes
loechel May 2, 2024
ae617dd
switch requests param to json, as Content-Type-Header is not set
loechel Aug 12, 2024
c600f08
switch requests param to json, as Content-Type-Header is not set, rem…
loechel Aug 12, 2024
6bf4ec9
switch requests param back to data, and explicitly set Content-Type-H…
loechel Aug 12, 2024
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
40 changes: 29 additions & 11 deletions src/edutap/wallet_google/api.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from .modelbase import GoogleWalletModel
from .modelbase import GoogleWalletObjectReference
from .modelbase import GoogleWalletObjectWithClassReferenceMixin
from .modelcore import GoogleWalletModel
from .models.primitives import Pagination
from .models.primitives.enums import State
from .models.primitives.notification import AddMessageRequest
from .models.primitives.notification import Message
from .models.primitives.message import AddMessageRequest
from .models.primitives.message import Message
from .registry import lookup_metadata
from .registry import lookup_model
from .registry import lookup_model_by_plural_name
Expand Down Expand Up @@ -170,6 +170,8 @@ def update(
url=session_manager.url(name, f"/{resource_id}"),
data=verified_json.encode("utf-8"),
)
logger.debug(verified_json.encode("utf-8"))
# print(verified_json.encode("utf-8"))
if response.status_code == 404:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commented print should go before merge.

raise LookupError(
f"Error 404, {name} {getattr(data, 'id', 'No ID')} not found: - {response.text}"
Expand Down Expand Up @@ -374,15 +376,31 @@ def save_link(
for obj in objs:
# first look if this is an object reference as dict
if isinstance(obj, dict) and "id" in obj and len(obj.keys()) <= 2:
obj = GoogleWalletObjectReference.model_validate(obj)
if isinstance(obj, GoogleWalletObjectReference):
payload[name].append(obj.model_dump(exclude_none=True, mode="json"))
obj = GoogleWalletObjectWithClassReferenceMixin.model_validate(obj)
if isinstance(obj, GoogleWalletObjectWithClassReferenceMixin):
payload[name].append(
obj.model_dump(
# explicitly set to model_dump(mode="json") instead of model_dump_json due to problems
# reported by jensens
mode="json",
exclude_none=True,
exclude_unset=True,
exclude_defaults=True,
)
)
continue

# otherwise it must be a registered model
model = lookup_model_by_plural_name(name)
obj = _validate_data(model, obj)
obj_json = obj.model_dump(exclude_none=True, mode="json")
obj_json = obj.model_dump(
# explicitly set to model_dump(mode="json") instead of model_dump_json due to problems
# reported by jensens
mode="json",
exclude_none=True,
exclude_unset=True,
exclude_defaults=True,
)
payload[name].append(obj_json)
claims = {
"iat": "",
Expand All @@ -394,9 +412,9 @@ def save_link(
}
signer = crypt.RSASigner.from_service_account_file(session_manager.credentials_file)
jwt_string = jwt.encode(signer, claims).decode("utf-8")
logger.debug(
"JWT-Length: %d, is less than recommenden 1800: %s",
logger.warning(
"JWT-Length: %d, is larger than recommenden 1800: %s",
Copy link
Contributor

@jensens jensens Apr 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if this is worth a warning. It works anyway if it is longer.

len(jwt_string),
loechel marked this conversation as resolved.
Show resolved Hide resolved
len(jwt_string) <= 1800,
len(jwt_string) >= 1800,
)
return f"{session_manager.save_url}/{jwt_string}"
144 changes: 126 additions & 18 deletions src/edutap/wallet_google/modelbase.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,151 @@
from pydantic import BaseModel
from pydantic import ConfigDict
from .modelcore import GoogleWalletModel
from .modelcore import GoogleWalletWithIdModel
from .models.primitives import CallbackOptions
from .models.primitives import GroupingInfo
from .models.primitives import Image
from .models.primitives import PassConstraints
from .models.primitives import SecurityAnimation
from .models.primitives.barcode import Barcode
from .models.primitives.barcode import RotatingBarcode
from .models.primitives.class_template_info import ClassTemplateInfo
from .models.primitives.data import ImageModuleData
from .models.primitives.data import InfoModuleData
from .models.primitives.data import LinksModuleData
from .models.primitives.data import TextModuleData
from .models.primitives.enums import MultipleDevicesAndHoldersAllowedStatus
from .models.primitives.enums import ViewUnlockRequirement
from .models.primitives.message import Message
from pydantic import Field


class GoogleWalletModel(BaseModel):
class GoogleWalletClassModel(GoogleWalletWithIdModel):
"""
Base model for all Google Wallet models.
BaseModel for all Google Wallet Class Models.
"""

model_config = ConfigDict(
extra="forbid",
use_enum_values=True,
# Templating and Visual Data
jensens marked this conversation as resolved.
Show resolved Hide resolved
classTemplateInfo: ClassTemplateInfo | None = None
imageModulesData: list[ImageModuleData] | None = None
textModulesData: list[TextModuleData] | None = None
linksModuleData: LinksModuleData | None = None
infoModuleData: InfoModuleData | None = Field(
description="deprecated",
deprecated=True,
exclude=True,
default=None,
)

# Callback Options
callbackOptions: CallbackOptions | None = None

# Smarttap Options
enableSmartTap: bool | None = None
redemptionIssuers: list[str] | None = None # string (int64 format)

# Barcode Options
securityAnimation: SecurityAnimation | None = None

# Security Options
viewUnlockRequirement: ViewUnlockRequirement = (
ViewUnlockRequirement.VIEW_UNLOCK_REQUIREMENT_UNSPECIFIED
)
allowMultipleUsersPerObject: bool | None = Field(
description="deprecated",
deprecated=True,
exclude=True,
default=None,
)
multipleDevicesAndHoldersAllowedStatus: MultipleDevicesAndHoldersAllowedStatus = (
MultipleDevicesAndHoldersAllowedStatus.STATUS_UNSPECIFIED
)

# Design Options
# hexBackgroundColor: str | None = None
# logo: Image | None = None
# wideLogo: Image | None = None
# heroImage: Image | None = None
wordMark: Image | None = Field(
description="deprecated",
deprecated=True,
exclude=True,
default=None,
)


class GoogleWalletWithIdModel(GoogleWalletModel):
class GoogleWalletObjectModel(GoogleWalletWithIdModel):
"""
Base model for Google Wallet models with an identifier.
Base model for all Google Wallet Object models.
"""

id: str
classId: str
version: str | None = Field(
description="deprecated", deprecated=True, exclude=True, default=None
)

groupingInfo: GroupingInfo | None = None

class GoogleWalletClassModel(GoogleWalletWithIdModel):
# Templating and Visual Data
imageModulesData: list[ImageModuleData] | None = None
textModulesData: list[TextModuleData] | None = None
linksModuleData: LinksModuleData | None = None
infoModuleData: InfoModuleData | None = Field(
description="deprecated",
deprecated=True,
exclude=True,
default=None,
)

# Security Options
passConstraints: PassConstraints | None = None

# Smart Tap Option
smartTapRedemptionValue: str | None = None

# Barcode Options
barcode: Barcode | None = None
rotatingBarcode: RotatingBarcode | None = None


class GoogleWalletObjectWithClassReferenceMixin(
GoogleWalletModel, GoogleWalletWithIdModel
):
"""
Base model for all Google Wallet Class models.
Mixin for all Google Wallet Object with a classReferences attribute, that reflects the whole class data.
"""

classReference: GoogleWalletClassModel | None = None

class GoogleWalletObjectModel(GoogleWalletWithIdModel):

class GoogleWalletMessageableMixin:
"""
Base model for all Google Wallet Object models.
Mixin for Google Wallet Classes or Objects that can retrieve Messages
"""

classId: str
messages: list[Message] | None = None


class GoogleWalletObjectReference(GoogleWalletWithIdModel):
class GoogleWalletStyleableClassMixin:
"""
Mixin for Google Wallet Classes that can be styled.
"""
Model for all Google Wallet Object references.

# Design Options
hexBackgroundColor: str | None = None
logo: Image | None = None
wideLogo: Image | None = None
heroImage: Image | None = None


class GoogleWalletStyleableObjectMixin:
"""
Mixin for Google Wallet Objects that can be styled.

TODO: Check if really all Objects that can be styled have all four attributes and also with this name.
Potential Override necessary for programLogo, ...
"""

classId: str | None = None
# Design Options
hexBackgroundColor: str | None = None
logo: Image | None = None
wideLogo: Image | None = None
heroImage: Image | None = None
40 changes: 40 additions & 0 deletions src/edutap/wallet_google/modelcore.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from pydantic import BaseModel
from pydantic import ConfigDict
from pydantic import Field


class GoogleWalletModel(BaseModel):
"""
Base Model for all Google Wallet Models.

Sets a model_config for all Google Wallet Models that enforce that all attributes must be explicitly modeled, and trying to set an unknown attribute would raise an Exception.
This Follows the Zen of Python (PEP 20) --> Explicit is better than implicit.
"""

model_config = ConfigDict(
extra="forbid",
# extra="ignore",
# use_enum_values=True,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

)


class GoogleWalletWithKindMixin(BaseModel):
"""
Mixin Class for Google Wallet Models with an deprecated kind identifier.
Explicit kind value should be provided by the inheriting concret class.
"""

kind: str | None = Field(
description="deprecated",
deprecated=True,
exclude=True,
default=None,
)


class GoogleWalletWithIdModel(BaseModel):
"""
Model for Google Wallet models with an identifier.
"""

id: str
82 changes: 39 additions & 43 deletions src/edutap/wallet_google/models/generic.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,62 @@
from ..modelbase import GoogleWalletClassModel
from ..modelbase import GoogleWalletMessageableMixin
from ..modelbase import GoogleWalletObjectModel
from ..modelbase import GoogleWalletStyleableClassMixin
from ..modelbase import GoogleWalletStyleableObjectMixin
from ..modelcore import GoogleWalletModel
from ..registry import register_model
from .primitives import CallbackOptions
from .primitives import GroupingInfo
from .primitives import Image
from .primitives import PassConstraints
from .primitives import SecurityAnimation
from .primitives.barcode import Barcode
from .primitives.barcode import RotatingBarcode
from .primitives.class_template_info import ClassTemplateInfo
from .primitives.data import AppLinkData
from .primitives.data import ImageModuleData
from .primitives.data import LinksModuleData
from .primitives.data import TextModuleData
from .primitives.datetime import TimeInterval
from .primitives.enums import GenericType
from .primitives.enums import MultipleDevicesAndHoldersAllowedStatus
from .primitives.enums import State
from .primitives.enums import ViewUnlockRequirement
from .primitives.localized_string import LocalizedString
from .primitives.notification import Notifications
from pydantic import Field


class ExpiryNotification(GoogleWalletModel):
"""
see: https://developers.google.com/wallet/generic/rest/v1/genericobject#expirynotification
"""

enableNotification: bool = False


class UpcomingNotification(GoogleWalletModel):
"""
see: https://developers.google.com/wallet/generic/rest/v1/genericobject#upcomingnotification
"""

enableNotification: bool = False


class Notifications(GoogleWalletModel):
"""
see: https://developers.google.com/wallet/generic/rest/v1/genericobject#notifications
"""

expiryNotification: ExpiryNotification | None = None
upcomingNotification: UpcomingNotification | None = None


@register_model(
"GenericClass", url_part="genericClass", plural="genericClasses", can_disable=False
)
class GenericClass(GoogleWalletClassModel):
class GenericClass(
GoogleWalletClassModel,
GoogleWalletStyleableClassMixin,
GoogleWalletMessageableMixin,
):
"""
see: https://developers.google.com/wallet/generic/rest/v1/genericclass
"""

classTemplateInfo: ClassTemplateInfo | None = None
imageModulesData: list[ImageModuleData] | None = None
textModulesData: list[TextModuleData] | None = None
linksModuleData: LinksModuleData | None = None
enableSmartTap: bool = False
redemptionIssuers: list[str] | None = None
securityAnimation: SecurityAnimation | None = None
multipleDevicesAndHoldersAllowedStatus: MultipleDevicesAndHoldersAllowedStatus = (
Field(default=MultipleDevicesAndHoldersAllowedStatus.STATUS_UNSPECIFIED)
)
callbackOptions: CallbackOptions | None = None
viewUnlockRequirement: ViewUnlockRequirement = Field(
default=ViewUnlockRequirement.VIEW_UNLOCK_REQUIREMENT_UNSPECIFIED
)


@register_model("GenericObject", url_part="genericObject")
class GenericObject(GoogleWalletObjectModel):
class GenericObject(
GoogleWalletObjectModel,
GoogleWalletStyleableObjectMixin,
GoogleWalletMessageableMixin,
):
"""
see: https://developers.google.com/wallet/generic/rest/v1/genericobject
"""
Expand All @@ -57,20 +65,8 @@ class GenericObject(GoogleWalletObjectModel):
cardTitle: LocalizedString | None = None
subheader: LocalizedString | None = None
header: LocalizedString | None = None
logo: Image | None = None
wideLogo: Image | None = None
hexBackgroundColor: str | None = None
notifications: Notifications | None = None
barcode: Barcode | None = None
heroImage: Image | None = None
validTimeInterval: TimeInterval | None = None
imageModulesData: list[ImageModuleData] | None = None
textModulesData: list[TextModuleData] | None = None
linksModuleData: LinksModuleData | None = None
appLinkData: AppLinkData | None = None
groupingInfo: GroupingInfo | None = None
smartTapRedemptionValue: str | None = None
rotatingBarcode: RotatingBarcode | None = None
state: State = Field(default=State.STATE_UNSPECIFIED)
hasUsers: bool | None = None
passConstraints: PassConstraints | None = None
Loading
Loading