mphapi 1.11.0__tar.gz → 1.12.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,12 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.1
2
2
  Name: mphapi
3
- Version: 1.11.0
3
+ Version: 1.12.2
4
4
  Summary: A Python interface to the MyPriceHealth API
5
5
  Author: David Archibald
6
6
  Author-email: davidarchibald@myprice.health
7
7
  Requires-Python: >=3.12,<4.0
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.12
10
- Classifier: Programming Language :: Python :: 3.13
11
10
  Requires-Dist: pydantic (>=2.6.4,<3.0.0)
12
11
  Requires-Dist: python-dotenv (>=1.1.1,<2.0.0)
13
12
  Requires-Dist: requests (>=2.31.0,<3.0.0)
@@ -1,5 +1,7 @@
1
1
  from .claim import * # noqa: F403, F401
2
2
  from .client import * # noqa: F403, F401
3
+ from .credentials import * # noqa: F403, F401
3
4
  from .date import * # noqa: F403, F401
5
+ from .fields import * # noqa: F403, F401
4
6
  from .pricing import * # noqa: F403, F401
5
7
  from .response import * # noqa: F403, F401
@@ -42,7 +42,7 @@ def test_client(snapshot: Snapshot):
42
42
  email="test-user@mypricehealth.com",
43
43
  # An id token with some minimal user info.
44
44
  # Will not work if tested against a service that expects a real id token.
45
- id_token="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImFhYSI6dHJ1ZX0.eyJpc3MiOiJ0ZXN0IGlzc3VlciIsImVtYWlsIjoidGVzdC11c2VyQG15cHJpY2VoZWFsdGguY29tIiwic3ViIjoidGVzdCBzdWJqZWN0IiwiYXVkIjoidGVzdCBhdWRpZW5jZSIsImV4cCI6MCwiaWF0IjowLCJpZCI6IjEyMzQ1Njc4OTAiLCJyb2xlcyI6WyJBZG1pbiJdfQ.iCx0YI9L9qo7KCkIq1w58C0mBdiCDhikPZsM3Mx78j11Ln3WxWpAnm0IomIRfrDVU1JczW8OjSj5os7igp9fYufZwObLV5N5eDd9LmYtXs2EcO8EFHcM7HeHx6lnkT1GxX-4J_946WbIdHYnpLyoXLZF_MZNfEwsN1UBG6YdaRcwfhF9vJHt6YM7-IoOcHvdLiEWv06Y9eC0v--_R_x8GwjSrE0Z9EyZw3hMz94QZ5VgUf8e89NexeVGIoD4pUOHuYmNIx1ca_UTIOg81exhhnh4g190jUSer5582YJc5Hx7gts3DyizRmAK8Glcwhnc7WKvZDQaSjtbHzIARnUKtw",
45
+ id_token="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImFhYSI6dHJ1ZX0.eyJpc3MiOiJ0ZXN0IGlzc3VlciIsImVtYWlsIjoidGVzdC11c2VyQG15cHJpY2VoZWFsdGguY29tIiwic3ViIjoidGVzdCBzdWJqZWN0IiwiYXVkIjoidGVzdCBhdWRpZW5jZSIsImV4cCI6MCwiaWF0IjowLCJsb2dpbl9pZCI6IjEyMzQ1Njc4OTAiLCJyb2xlcyI6WyJBZG1pbiJdfQ.iCx0YI9L9qo7KCkIq1w58C0mBdiCDhikPZsM3Mx78j11Ln3WxWpAnm0IomIRfrDVU1JczW8OjSj5os7igp9fYufZwObLV5N5eDd9LmYtXs2EcO8EFHcM7HeHx6lnkT1GxX-4J_946WbIdHYnpLyoXLZF_MZNfEwsN1UBG6YdaRcwfhF9vJHt6YM7-IoOcHvdLiEWv06Y9eC0v--_R_x8GwjSrE0Z9EyZw3hMz94QZ5VgUf8e89NexeVGIoD4pUOHuYmNIx1ca_UTIOg81exhhnh4g190jUSer5582YJc5Hx7gts3DyizRmAK8Glcwhnc7WKvZDQaSjtbHzIARnUKtw",
46
46
  refresh_token="fake-refresh-token",
47
47
  expires_at=sys.float_info.max,
48
48
  )
@@ -1,8 +1,11 @@
1
+ import base64
2
+ import binascii
1
3
  import getpass
2
4
  import json
3
5
  import os
4
6
  import time
5
7
  from pathlib import Path
8
+ from typing import Any
6
9
 
7
10
  import requests
8
11
  from pydantic import BaseModel, Field, RootModel
@@ -140,6 +143,23 @@ class RefreshTokenResult(BaseModel):
140
143
  """Your Google Cloud project ID."""
141
144
 
142
145
 
146
+ class Token(BaseModel):
147
+ """
148
+ The decoded ID token from SignInResult or RefreshTokenResult.
149
+ """
150
+
151
+ issuer: str
152
+ email: str
153
+ subject: str
154
+ audience: str
155
+ authorized_party: str
156
+ expiration: int
157
+ issued_at: int
158
+ login_id: str # not to be confused with the user_id claim, the firebase id
159
+ roles: list[str]
160
+ login_name: str # not to be confused with the name claim, the name in firebase
161
+
162
+
143
163
  def get_credentials(api_key: str, referer: str) -> Credentials:
144
164
  credentials_path = get_credentials_path()
145
165
 
@@ -254,3 +274,47 @@ def sign_in(api_key: str, email: str, password: str) -> RawCredentials:
254
274
  )
255
275
 
256
276
  return credentials
277
+
278
+
279
+ # decode_jwt will parse a JWT payload without verifying that it is cryptographically valid.
280
+ # Code is simplified from https://github.com/jpadilla/pyjwt
281
+ def decode_jwt(jwt: str) -> Token:
282
+ try:
283
+ signing_input, _ = jwt.rsplit(".", 1)
284
+ _, payload_segment = signing_input.split(".", 1)
285
+ except Exception as err:
286
+ raise Exception("Expected token with 3 segments") from err
287
+
288
+ try:
289
+ payload_decoded = base64url_decode(payload_segment)
290
+ except (TypeError, binascii.Error) as err:
291
+ raise Exception("Invalid payload padding") from err
292
+
293
+ try:
294
+ payload: dict[str, Any] = json.loads(payload_decoded)
295
+ except ValueError as e:
296
+ raise Exception(f"Invalid payload string: {e}") from e
297
+
298
+ return Token(
299
+ issuer=payload.get("iss", ""),
300
+ email=payload.get("email", ""),
301
+ subject=payload.get("sub", ""),
302
+ audience=payload.get("aud", ""),
303
+ authorized_party=payload.get("azp", ""),
304
+ expiration=payload.get("exp", 0),
305
+ issued_at=payload.get("iat", 0),
306
+ login_id=payload.get("login_id", ""),
307
+ roles=payload.get("roles", []),
308
+ login_name=payload.get("login_name", ""),
309
+ )
310
+
311
+
312
+ def base64url_decode(input: str) -> bytes:
313
+ input_bytes = input.encode("utf-8")
314
+
315
+ rem = len(input_bytes) % 4
316
+
317
+ if rem > 0:
318
+ input_bytes += b"=" * (4 - rem)
319
+
320
+ return base64.urlsafe_b64decode(input_bytes)
@@ -4,7 +4,6 @@ from typing import Annotated, Optional
4
4
  from pydantic import BaseModel, Field
5
5
 
6
6
  from .client import PriceConfig
7
- from .date import DateTime
8
7
  from .fields import camel_case_model_config, field_name
9
8
  from .response import ResponseError
10
9
 
@@ -464,8 +463,6 @@ class StepAndStatus(BaseModel):
464
463
  class ClaimStatus(StepAndStatus):
465
464
  model_config = camel_case_model_config
466
465
 
467
- updated_by: Optional[str] = None
468
- updated_at: Optional[DateTime] = None
469
466
  pricing: Optional[Pricing] = None
470
467
  error: Optional[ResponseError] = None
471
468
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "mphapi"
3
- version = "1.11.0"
3
+ version = "v1.12.2"
4
4
  description = "A Python interface to the MyPriceHealth API"
5
5
  authors = ["David Archibald <davidarchibald@myprice.health>"]
6
6
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes