Skip to content

Commit 7c7c8ea

Browse files
Merge pull request #45 from alexanderjordanbaker/RawEnumValues
Allow accessing new Enum values without requiring a library upgrade
2 parents c631d90 + 244a0c3 commit 7c7c8ea

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+989
-118
lines changed

appstoreserverlibrary/api_client.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
import calendar
44
import datetime
55
from enum import IntEnum
6-
from typing import Dict, List, Optional, Type, TypeVar, Union
6+
from typing import Any, Dict, List, Optional, Type, TypeVar, Union
77
from attr import define
8-
import cattrs
98
import requests
109

1110
import jwt
1211
from cryptography.hazmat.backends import default_backend
1312
from cryptography.hazmat.primitives import serialization
13+
14+
from appstoreserverlibrary.models.LibraryUtility import _get_cattrs_converter
1415
from .models.CheckTestNotificationResponse import CheckTestNotificationResponse
1516
from .models.ConsumptionRequest import ConsumptionRequest
1617

@@ -94,11 +95,18 @@ class APIError(IntEnum):
9495
@define
9596
class APIException(Exception):
9697
http_status_code: int
97-
api_error: APIError
98+
api_error: Optional[APIError]
99+
raw_api_error: Optional[int]
98100

99-
def __init__(self, http_status_code: int, api_error: APIError = None):
101+
def __init__(self, http_status_code: int, raw_api_error: Optional[int] = None):
100102
self.http_status_code = http_status_code
101-
self.api_error = api_error
103+
self.raw_api_error = raw_api_error
104+
self.api_error = None
105+
try:
106+
if raw_api_error is not None:
107+
self.api_error = APIError(raw_api_error)
108+
except ValueError:
109+
pass
102110

103111
class AppStoreServerAPIClient:
104112
def __init__(self, signing_key: bytes, key_id: str, issuer_id: str, bundle_id: str, environment: Environment):
@@ -127,32 +135,35 @@ def _generate_token(self) -> str:
127135

128136
def _make_request(self, path: str, method: str, queryParameters: Dict[str, Union[str, List[str]]], body, destination_class: Type[T]) -> T:
129137
url = self._base_url + path
130-
json = cattrs.unstructure(body) if body != None else None
138+
c = _get_cattrs_converter(type(body)) if body != None else None
139+
json = c.unstructure(body) if body != None else None
131140
headers = {
132141
'User-Agent': "app-store-server-library/python/0.1",
133142
'Authorization': 'Bearer ' + self._generate_token(),
134143
'Accept': 'application/json'
135144
}
136145

137-
response = requests.request(method, url, params=queryParameters, headers=headers, json=json)
146+
response = self._execute_request(method, url, queryParameters, headers, json)
138147
if response.status_code >= 200 and response.status_code < 300:
139148
if destination_class == None:
140149
return
150+
c = _get_cattrs_converter(destination_class)
141151
response_body = response.json()
142-
return cattrs.structure(response_body, destination_class)
152+
return c.structure(response_body, destination_class)
143153
else:
144154
# Best effort parsing of the response body
145155
if not 'content-type' in response.headers or response.headers['content-type'] != 'application/json':
146156
raise APIException(response.status_code)
147157
try:
148158
response_body = response.json()
149-
errorValue = APIError(response_body['errorCode'])
150-
raise APIException(response.status_code, errorValue)
159+
raise APIException(response.status_code, response_body['errorCode'])
151160
except APIException as e:
152161
raise e
153162
except Exception:
154163
raise APIException(response.status_code)
155164

165+
def _execute_request(self, method: str, url: str, params: Dict[str, Union[str, List[str]]], headers: Dict[str, str], json: Dict[str, Any]) -> requests.Response:
166+
return requests.request(method, url, params=params, headers=headers, json=json)
156167

157168
def extend_renewal_date_for_all_active_subscribers(self, mass_extend_renewal_date_request: MassExtendRenewalDateRequest) -> MassExtendRenewalDateResponse:
158169
"""
@@ -163,7 +174,7 @@ def extend_renewal_date_for_all_active_subscribers(self, mass_extend_renewal_dat
163174
:return: A response that indicates the server successfully received the subscription-renewal-date extension request.
164175
:throws APIException: If a response was returned indicating the request could not be processed
165176
"""
166-
return self._make_request("/inApps/v1/subscriptions/extend/mass/", "POST", {}, mass_extend_renewal_date_request, MassExtendRenewalDateResponse)
177+
return self._make_request("/inApps/v1/subscriptions/extend/mass", "POST", {}, mass_extend_renewal_date_request, MassExtendRenewalDateResponse)
167178

168179
def extend_subscription_renewal_date(self, original_transaction_id: str, extend_renewal_date_request: ExtendRenewalDateRequest) -> ExtendRenewalDateResponse:
169180
"""

appstoreserverlibrary/models/AccountTenure.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
from enum import IntEnum
44

5-
class AccountTenure(IntEnum):
5+
from .LibraryUtility import AppStoreServerLibraryEnumMeta
6+
7+
class AccountTenure(IntEnum, metaclass=AppStoreServerLibraryEnumMeta):
68
"""
79
The age of the customer's account.
810

appstoreserverlibrary/models/AppTransaction.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,30 @@
44
from attr import define
55
import attr
66

7+
from .LibraryUtility import AttrsRawValueAware
8+
79
from .Environment import Environment
810

911
@define
10-
class AppTransaction:
12+
class AppTransaction(AttrsRawValueAware):
1113
"""
1214
Information that represents the customer’s purchase of the app, cryptographically signed by the App Store.
1315
1416
https://developer.apple.com/documentation/storekit/apptransaction
1517
"""
1618

17-
receiptType: Optional[Environment] = attr.ib(default=None)
19+
receiptType: Optional[Environment] = Environment.create_main_attr('rawReceiptType')
1820
"""
1921
The server environment that signs the app transaction.
2022
2123
https://developer.apple.com/documentation/storekit/apptransaction/3963901-environment
2224
"""
2325

26+
rawReceiptType: Optional[str] = Environment.create_raw_attr('receiptType')
27+
"""
28+
See receiptType
29+
"""
30+
2431
appAppleId: Optional[int] = attr.ib(default=None)
2532
"""
2633
The unique identifier the App Store uses to identify the app.

appstoreserverlibrary/models/AutoRenewStatus.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
from enum import IntEnum
44

5-
class AutoRenewStatus(IntEnum):
5+
from .LibraryUtility import AppStoreServerLibraryEnumMeta
6+
7+
class AutoRenewStatus(IntEnum, metaclass=AppStoreServerLibraryEnumMeta):
68
"""
79
The renewal status for an auto-renewable subscription.
810

appstoreserverlibrary/models/ConsumptionRequest.py

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@
33

44
from attr import define
55
import attr
6-
from .AccountTenure import AccountTenure
76

7+
from .AccountTenure import AccountTenure
88
from .ConsumptionStatus import ConsumptionStatus
99
from .DeliveryStatus import DeliveryStatus
10+
from .LibraryUtility import AttrsRawValueAware
1011
from .LifetimeDollarsPurchased import LifetimeDollarsPurchased
1112
from .LifetimeDollarsRefunded import LifetimeDollarsRefunded
1213
from .Platform import Platform
1314
from .PlayTime import PlayTime
1415
from .UserStatus import UserStatus
1516

1617
@define
17-
class ConsumptionRequest:
18+
class ConsumptionRequest(AttrsRawValueAware):
1819
"""
1920
The request body containing consumption information.
2021
@@ -28,72 +29,112 @@ class ConsumptionRequest:
2829
https://developer.apple.com/documentation/appstoreserverapi/customerconsented
2930
"""
3031

31-
consumptionStatus: Optional[ConsumptionStatus] = attr.ib(default=None)
32+
consumptionStatus: Optional[ConsumptionStatus] = ConsumptionStatus.create_main_attr('rawConsumptionStatus')
3233
"""
3334
A value that indicates the extent to which the customer consumed the in-app purchase.
3435
3536
https://developer.apple.com/documentation/appstoreserverapi/consumptionstatus
3637
"""
3738

38-
platform: Optional[Platform] = attr.ib(default=None)
39+
rawConsumptionStatus: Optional[int] = ConsumptionStatus.create_raw_attr('consumptionStatus')
40+
"""
41+
See consumptionStatus
42+
"""
43+
44+
platform: Optional[Platform] = Platform.create_main_attr('rawPlatform')
3945
"""
4046
A value that indicates the platform on which the customer consumed the in-app purchase.
4147
4248
https://developer.apple.com/documentation/appstoreserverapi/platform
4349
"""
4450

51+
rawPlatform: Optional[int] = Platform.create_raw_attr('platform')
52+
"""
53+
See platform
54+
"""
55+
4556
sampleContentProvided: Optional[bool] = attr.ib(default=None)
4657
"""
4758
A Boolean value that indicates whether you provided, prior to its purchase, a free sample or trial of the content, or information about its functionality.
4859
4960
https://developer.apple.com/documentation/appstoreserverapi/samplecontentprovided
5061
"""
5162

52-
deliveryStatus: Optional[DeliveryStatus] = attr.ib(default=None)
63+
deliveryStatus: Optional[DeliveryStatus] = DeliveryStatus.create_main_attr('rawDeliveryStatus')
5364
"""
5465
A value that indicates whether the app successfully delivered an in-app purchase that works properly.
5566
5667
https://developer.apple.com/documentation/appstoreserverapi/deliverystatus
5768
"""
5869

70+
rawDeliveryStatus: Optional[int] = DeliveryStatus.create_raw_attr('deliveryStatus')
71+
"""
72+
See deliveryStatus
73+
"""
74+
5975
appAccountToken: Optional[str] = attr.ib(default=None)
6076
"""
6177
The UUID that an app optionally generates to map a customer's in-app purchase with its resulting App Store transaction.
6278
6379
https://developer.apple.com/documentation/appstoreserverapi/appaccounttoken
6480
"""
6581

66-
accountTenure: Optional[AccountTenure] = attr.ib(default=None)
82+
accountTenure: Optional[AccountTenure] = AccountTenure.create_main_attr('rawAccountTenure')
6783
"""
6884
The age of the customer's account.
6985
7086
https://developer.apple.com/documentation/appstoreserverapi/accounttenure
7187
"""
7288

73-
playTime: Optional[PlayTime] = attr.ib(default=None)
89+
rawAccountTenure: Optional[int] = AccountTenure.create_raw_attr('accountTenure')
90+
"""
91+
See accountTenure
92+
"""
93+
94+
playTime: Optional[PlayTime] = PlayTime.create_main_attr('rawPlayTime')
7495
"""
7596
A value that indicates the amount of time that the customer used the app.
7697
7798
https://developer.apple.com/documentation/appstoreserverapi/consumptionrequest
7899
"""
79100

80-
lifetimeDollarsRefunded: Optional[LifetimeDollarsRefunded] = attr.ib(default=None)
101+
rawPlayTime: Optional[int] = PlayTime.create_raw_attr('playTime')
102+
"""
103+
See playTime
104+
"""
105+
106+
lifetimeDollarsRefunded: Optional[LifetimeDollarsRefunded] = LifetimeDollarsRefunded.create_main_attr('rawLifetimeDollarsRefunded')
81107
"""
82108
A value that indicates the total amount, in USD, of refunds the customer has received, in your app, across all platforms.
83109
84110
https://developer.apple.com/documentation/appstoreserverapi/lifetimedollarsrefunded
85111
"""
86112

87-
lifetimeDollarsPurchased: Optional[LifetimeDollarsPurchased] = attr.ib(default=None)
113+
rawLifetimeDollarsRefunded: Optional[int] = LifetimeDollarsRefunded.create_raw_attr('lifetimeDollarsRefunded')
114+
"""
115+
See lifetimeDollarsRefunded
116+
"""
117+
118+
lifetimeDollarsPurchased: Optional[LifetimeDollarsPurchased] = LifetimeDollarsPurchased.create_main_attr('rawLifetimeDollarsPurchased')
88119
"""
89120
A value that indicates the total amount, in USD, of in-app purchases the customer has made in your app, across all platforms.
90121
91122
https://developer.apple.com/documentation/appstoreserverapi/lifetimedollarspurchased
92123
"""
93124

94-
userStatus: Optional[UserStatus] = attr.ib(default=None)
125+
rawLifetimeDollarsPurchased: Optional[int] = LifetimeDollarsPurchased.create_raw_attr('lifetimeDollarsPurchased')
126+
"""
127+
See lifetimeDollarsPurchased
128+
"""
129+
130+
userStatus: Optional[UserStatus] = UserStatus.create_main_attr('rawUserStatus')
95131
"""
96132
The status of the customer's account.
97133
98134
https://developer.apple.com/documentation/appstoreserverapi/userstatus
135+
"""
136+
137+
rawUserStatus: Optional[int] = UserStatus.create_raw_attr('userStatus')
138+
"""
139+
See userStatus
99140
"""

appstoreserverlibrary/models/ConsumptionStatus.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
from enum import IntEnum
44

5-
class ConsumptionStatus(IntEnum):
5+
from .LibraryUtility import AppStoreServerLibraryEnumMeta
6+
7+
class ConsumptionStatus(IntEnum, metaclass=AppStoreServerLibraryEnumMeta):
68
"""
79
A value that indicates the extent to which the customer consumed the in-app purchase.
810

appstoreserverlibrary/models/Data.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,25 @@
66

77
from .Environment import Environment
88
from .Status import Status
9+
from .LibraryUtility import AttrsRawValueAware
910

1011
@define
11-
class Data:
12+
class Data(AttrsRawValueAware):
1213
"""
1314
The app metadata and the signed renewal and transaction information.
1415
1516
https://developer.apple.com/documentation/appstoreservernotifications/data
1617
"""
17-
18-
environment: Optional[Environment] = attr.ib(default=None)
18+
environment: Optional[Environment] = Environment.create_main_attr('rawEnvironment')
1919
"""
2020
The server environment that the notification applies to, either sandbox or production.
2121
2222
https://developer.apple.com/documentation/appstoreservernotifications/environment
2323
"""
24+
rawEnvironment: Optional[str] = Environment.create_raw_attr('environment')
25+
"""
26+
See environment
27+
"""
2428

2529
appAppleId: Optional[int] = attr.ib(default=None)
2630
"""
@@ -57,9 +61,14 @@ class Data:
5761
https://developer.apple.com/documentation/appstoreserverapi/jwsrenewalinfo
5862
"""
5963

60-
status: Optional[Status] = attr.ib(default=None)
64+
status: Optional[Status] = Status.create_main_attr('rawStatus')
6165
"""
6266
The status of an auto-renewable subscription as of the signedDate in the responseBodyV2DecodedPayload.
6367
6468
https://developer.apple.com/documentation/appstoreservernotifications/status
69+
"""
70+
71+
rawStatus: Optional[int] = Status.create_raw_attr('status')
72+
"""
73+
See status
6574
"""

appstoreserverlibrary/models/DeliveryStatus.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
from enum import IntEnum
44

5-
class DeliveryStatus(IntEnum):
5+
from .LibraryUtility import AppStoreServerLibraryEnumMeta
6+
7+
class DeliveryStatus(IntEnum, metaclass=AppStoreServerLibraryEnumMeta):
68
"""
79
A value that indicates whether the app successfully delivered an in-app purchase that works properly.
810

appstoreserverlibrary/models/Environment.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
from enum import Enum
44

5-
class Environment(str, Enum):
5+
from .LibraryUtility import AppStoreServerLibraryEnumMeta
6+
7+
class Environment(str, Enum, metaclass=AppStoreServerLibraryEnumMeta):
68
"""
79
The server environment, either sandbox or production.
810

appstoreserverlibrary/models/ExpirationIntent.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
from enum import IntEnum
44

5-
class ExpirationIntent(IntEnum):
5+
from .LibraryUtility import AppStoreServerLibraryEnumMeta
6+
7+
class ExpirationIntent(IntEnum, metaclass=AppStoreServerLibraryEnumMeta):
68
"""
79
The reason an auto-renewable subscription expired.
810

0 commit comments

Comments
 (0)