sweatstack 0.92.0__tar.gz → 0.93.0__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.
Files changed (105) hide show
  1. {sweatstack-0.92.0 → sweatstack-0.93.0}/CHANGELOG.md +18 -0
  2. {sweatstack-0.92.0 → sweatstack-0.93.0}/PKG-INFO +1 -1
  3. {sweatstack-0.92.0 → sweatstack-0.93.0}/pyproject.toml +1 -1
  4. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/client.py +2 -0
  5. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/openapi_schemas.py +41 -25
  6. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/oauth.py +2 -1
  7. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/profile.py +12 -8
  8. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/schemas.py +1 -0
  9. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_account_status.py +17 -7
  10. {sweatstack-0.92.0 → sweatstack-0.93.0}/uv.lock +1 -1
  11. {sweatstack-0.92.0 → sweatstack-0.93.0}/.git-blame-ignore-revs +0 -0
  12. {sweatstack-0.92.0 → sweatstack-0.93.0}/.github/workflows/ci.yml +0 -0
  13. {sweatstack-0.92.0 → sweatstack-0.93.0}/.gitignore +0 -0
  14. {sweatstack-0.92.0 → sweatstack-0.93.0}/.gitleaks.toml +0 -0
  15. {sweatstack-0.92.0 → sweatstack-0.93.0}/.python-version +0 -0
  16. {sweatstack-0.92.0 → sweatstack-0.93.0}/AGENTS.md +0 -0
  17. {sweatstack-0.92.0 → sweatstack-0.93.0}/CONTRIBUTING.md +0 -0
  18. {sweatstack-0.92.0 → sweatstack-0.93.0}/DEVELOPMENT.md +0 -0
  19. {sweatstack-0.92.0 → sweatstack-0.93.0}/LICENSE +0 -0
  20. {sweatstack-0.92.0 → sweatstack-0.93.0}/Makefile +0 -0
  21. {sweatstack-0.92.0 → sweatstack-0.93.0}/README.md +0 -0
  22. {sweatstack-0.92.0 → sweatstack-0.93.0}/evals/__init__.py +0 -0
  23. {sweatstack-0.92.0 → sweatstack-0.93.0}/evals/agent/__init__.py +0 -0
  24. {sweatstack-0.92.0 → sweatstack-0.93.0}/evals/agent/agent.py +0 -0
  25. {sweatstack-0.92.0 → sweatstack-0.93.0}/evals/agent/cases.py +0 -0
  26. {sweatstack-0.92.0 → sweatstack-0.93.0}/evals/agent/evaluators.py +0 -0
  27. {sweatstack-0.92.0 → sweatstack-0.93.0}/evals/agent/harness.py +0 -0
  28. {sweatstack-0.92.0 → sweatstack-0.93.0}/evals/agent/lifecycle.py +0 -0
  29. {sweatstack-0.92.0 → sweatstack-0.93.0}/evals/agent/run.py +0 -0
  30. {sweatstack-0.92.0 → sweatstack-0.93.0}/examples/fastapi_webhooks_example.py +0 -0
  31. {sweatstack-0.92.0 → sweatstack-0.93.0}/examples/send_webhook.py +0 -0
  32. {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/001a_tests.md +0 -0
  33. {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/001b_metadata.md +0 -0
  34. {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/001c_dailies.md +0 -0
  35. {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/002_TYPED_EXCEPTIONS.md +0 -0
  36. {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/003_trace_test_linking.md +0 -0
  37. {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/004_codebase_hygiene.md +0 -0
  38. {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/005_ost_sport_bridge.md +0 -0
  39. {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/006_output_backends.md +0 -0
  40. {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/007_account_status_userinfo_portal.md +0 -0
  41. {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/008_sdk_documentation.md +0 -0
  42. {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/009_road_to_1_0.md +0 -0
  43. {sweatstack-0.92.0 → sweatstack-0.93.0}/skills/sweatstack-python/SKILL.md +0 -0
  44. {sweatstack-0.92.0 → sweatstack-0.93.0}/skills/sweatstack-python/api.md +0 -0
  45. {sweatstack-0.92.0 → sweatstack-0.93.0}/skills/sweatstack-python/data-models.md +0 -0
  46. {sweatstack-0.92.0 → sweatstack-0.93.0}/skills/sweatstack-python/fastapi.md +0 -0
  47. {sweatstack-0.92.0 → sweatstack-0.93.0}/skills/sweatstack-python/streamlit.md +0 -0
  48. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/__init__.py +0 -0
  49. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/_docs.py +0 -0
  50. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/_frames.py +0 -0
  51. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/_renames.py +0 -0
  52. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/_transport.py +0 -0
  53. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/cli.py +0 -0
  54. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/constants.py +0 -0
  55. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/exceptions.py +0 -0
  56. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/fastapi/__init__.py +0 -0
  57. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/fastapi/access_token_cache.py +0 -0
  58. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/fastapi/config.py +0 -0
  59. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/fastapi/dependencies.py +0 -0
  60. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/fastapi/models.py +0 -0
  61. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/fastapi/routes.py +0 -0
  62. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/fastapi/session.py +0 -0
  63. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/fastapi/token_stores.py +0 -0
  64. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/fastapi/webhooks.py +0 -0
  65. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/py.typed +0 -0
  66. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/__init__.py +0 -0
  67. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/_app_metadata.py +0 -0
  68. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/_base.py +0 -0
  69. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/activities.py +0 -0
  70. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/dailies.py +0 -0
  71. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/portal.py +0 -0
  72. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/teams.py +0 -0
  73. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/tests.py +0 -0
  74. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/traces.py +0 -0
  75. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/users.py +0 -0
  76. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/streamlit.py +0 -0
  77. {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/utils.py +0 -0
  78. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/__init__.py +0 -0
  79. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_access_token_cache.py +0 -0
  80. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_dailies.py +0 -0
  81. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_docs_check.py +0 -0
  82. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_docstrings.py +0 -0
  83. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_dtype_conversion.py +0 -0
  84. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_exceptions.py +0 -0
  85. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_frames.py +0 -0
  86. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_identity.py +0 -0
  87. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_longitudinal_mean_max_after.py +0 -0
  88. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_metadata.py +0 -0
  89. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_output.py +0 -0
  90. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_portal.py +0 -0
  91. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_public_hygiene.py +0 -0
  92. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_public_surface.py +0 -0
  93. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_renames.py +0 -0
  94. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_resources.py +0 -0
  95. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_segmentation.py +0 -0
  96. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_skill.py +0 -0
  97. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_sport_ost.py +0 -0
  98. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_teams.py +0 -0
  99. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_tests.py +0 -0
  100. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_trace_test_linking.py +0 -0
  101. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_transport.py +0 -0
  102. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_webhooks.py +0 -0
  103. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_wire_names.py +0 -0
  104. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_write_timezone_validation.py +0 -0
  105. {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/typing/output_types.py +0 -0
@@ -6,6 +6,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
 
9
+ ## [0.93.0] - 2026-10-09
10
+
11
+ Issues are only about the data your app uses. Requires the SweatStack API release that adds
12
+ `capabilities` to applications (see the platform changelog).
13
+
14
+ ### Changed
15
+
16
+ - **BREAKING: `issue.status` is an `IssueStatus`**, `action_required` or `syncing`, on
17
+ `oauth.userinfo()` and `profile.status()`. The `unavailable` issues
18
+ (`activity_history_unavailable`, `dailies_unavailable`, `workouts_unavailable`) are gone: what
19
+ no connected source provides is in `capabilities`, where `unavailable` means the user could
20
+ still connect a source that provides it. Three new codes, each `action_required` with a
21
+ `destination`: `activities_blocked_upstream`, `dailies_no_source`, `workouts_no_source`. A
22
+ client on 0.90.0 or later accepts them without an upgrade (`StatusIssueCode` is open).
23
+ - `ApplicationCreate.capabilities` and `ApplicationResponse.capabilities`: the capabilities an
24
+ app declares it uses (`list[Capability]`; omit it for the default, `activities` and
25
+ `activity_history`). SweatStack reports issues only for these.
26
+
9
27
  ## [0.92.0] - 2026-10-08
10
28
 
11
29
  Portal links are created with the user's token, when the user acts. Requires the SweatStack API
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sweatstack
3
- Version: 0.92.0
3
+ Version: 0.93.0
4
4
  Summary: The official Python client for SweatStack
5
5
  Project-URL: Homepage, https://sweatstack.no
6
6
  Project-URL: Documentation, https://docs.sweatstack.no/learn/python/
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sweatstack"
3
- version = "0.92.0"
3
+ version = "0.93.0"
4
4
  description = "The official Python client for SweatStack"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -60,6 +60,7 @@ from .schemas import (
60
60
  CapabilityStatus,
61
61
  DailyMeasure,
62
62
  DailyResponse,
63
+ IssueStatus,
63
64
  Marker,
64
65
  Metric,
65
66
  Modifier,
@@ -1103,6 +1104,7 @@ __all__ = sorted(
1103
1104
  "CapabilityStatus",
1104
1105
  "DailyMeasure",
1105
1106
  "DailyResponse",
1107
+ "IssueStatus",
1106
1108
  "Marker",
1107
1109
  "Metric",
1108
1110
  "Modifier",
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: openapi.json
3
- # timestamp: 2026-10-08T18:52:13+00:00
3
+ # timestamp: 2026-10-09T08:30:22+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
  from open_sport_taxonomy.pydantic import SportField # OST sport type (see schemas.py)
@@ -44,26 +44,6 @@ class AltitudeSummary(BaseModel):
44
44
  max: int | float | None = Field(None, title='Max')
45
45
 
46
46
 
47
- class ApplicationCreate(BaseModel):
48
- name: constr(min_length=1, max_length=100) = Field(..., title='Name')
49
- description: constr(max_length=500) | None = Field(None, title='Description')
50
- redirect_uris: list[str] | None = Field(None, title='Redirect Uris')
51
- page: str | None = Field(None, title='Page')
52
- generate_secret: bool | None = Field(False, title='Generate Secret')
53
-
54
-
55
- class ApplicationCreateOrUpdate(BaseModel):
56
- name: str = Field(..., title='Name')
57
- description: str | None = Field(None, title='Description')
58
- url: AnyUrl | None = Field(None, title='Url')
59
- image: AnyUrl | None = Field(None, title='Image')
60
- redirect_uris: list[str] | None = Field(None, title='Redirect Uris')
61
- privacy_statement: AnyUrl | None = Field(None, title='Privacy Statement')
62
- brand_color: str | None = Field(None, title='Brand Color')
63
- webhook_endpoints: list[AnyUrl] | None = Field(None, title='Webhook Endpoints')
64
- webhook_secret: SecretStr | None = Field(None, title='Webhook Secret')
65
-
66
-
67
47
  class ApplicationListItem(BaseModel):
68
48
  id: str = Field(..., title='Id')
69
49
  name: str = Field(..., title='Name')
@@ -432,6 +412,11 @@ class IntervalsIcuWebhookEventType(Enum):
432
412
  FITNESS_UPDATED = 'FITNESS_UPDATED'
433
413
 
434
414
 
415
+ class IssueStatus(Enum):
416
+ action_required = 'action_required'
417
+ syncing = 'syncing'
418
+
419
+
435
420
  class ManagedUserCreate(BaseModel):
436
421
  first_name: str = Field(..., title='First Name')
437
422
  last_name: str | None = Field(..., title='Last Name')
@@ -619,18 +604,18 @@ class SpeedSummary(BaseModel):
619
604
  class StatusIssueCode(Enum):
620
605
  no_source_connected = 'no_source_connected'
621
606
  activities_not_granted = 'activities_not_granted'
607
+ activities_blocked_upstream = 'activities_blocked_upstream'
622
608
  activity_history_not_granted = 'activity_history_not_granted'
623
609
  dailies_not_granted = 'dailies_not_granted'
610
+ dailies_no_source = 'dailies_no_source'
624
611
  workouts_not_granted = 'workouts_not_granted'
612
+ workouts_no_source = 'workouts_no_source'
625
613
  sync_pending = 'sync_pending'
626
- activity_history_unavailable = 'activity_history_unavailable'
627
- dailies_unavailable = 'dailies_unavailable'
628
- workouts_unavailable = 'workouts_unavailable'
629
614
 
630
615
 
631
616
  class StatusIssueResponse(BaseModel):
632
617
  code: StatusIssueCode
633
- status: CapabilityStatus
618
+ status: IssueStatus
634
619
  message: str = Field(..., title='Message')
635
620
  destination: PortalDestination | None = None
636
621
 
@@ -944,6 +929,32 @@ class ActivitySummarySummary(BaseModel):
944
929
  smo2: Smo2Summary | None = None
945
930
 
946
931
 
932
+ class ApplicationCreate(BaseModel):
933
+ name: constr(min_length=1, max_length=100) = Field(..., title='Name')
934
+ description: constr(max_length=500) | None = Field(None, title='Description')
935
+ redirect_uris: list[str] | None = Field(None, title='Redirect Uris')
936
+ capabilities: list[Capability] | None = Field(
937
+ None,
938
+ description="The capabilities your app uses: SweatStack reports account-status issues only for these (plan 075). Omit it for the default, activities and activity_history. An empty list means no issues at all. Change it later in the app's settings.",
939
+ title='Capabilities',
940
+ )
941
+ page: str | None = Field(None, title='Page')
942
+ generate_secret: bool | None = Field(False, title='Generate Secret')
943
+
944
+
945
+ class ApplicationCreateOrUpdate(BaseModel):
946
+ name: str = Field(..., title='Name')
947
+ description: str | None = Field(None, title='Description')
948
+ url: AnyUrl | None = Field(None, title='Url')
949
+ image: AnyUrl | None = Field(None, title='Image')
950
+ redirect_uris: list[str] | None = Field(None, title='Redirect Uris')
951
+ privacy_statement: AnyUrl | None = Field(None, title='Privacy Statement')
952
+ brand_color: str | None = Field(None, title='Brand Color')
953
+ webhook_endpoints: list[AnyUrl] | None = Field(None, title='Webhook Endpoints')
954
+ webhook_secret: SecretStr | None = Field(None, title='Webhook Secret')
955
+ capabilities: list[Capability] | None = Field(None, title='Capabilities')
956
+
957
+
947
958
  class ApplicationCreateResponse(BaseModel):
948
959
  id: str = Field(..., title='Id')
949
960
  name: str = Field(..., title='Name')
@@ -959,6 +970,11 @@ class ApplicationCreateResponse(BaseModel):
959
970
  description='The App Directory is retired. Reflects the last stored listing; removed on 2026-10-12.',
960
971
  title='Published',
961
972
  )
973
+ capabilities: list[Capability] | None = Field(
974
+ None,
975
+ description='The capabilities this app uses. SweatStack reports account-status issues only for these. Default: activities and activity_history.',
976
+ title='Capabilities',
977
+ )
962
978
  webhook_endpoints: list[AnyUrl] | None = Field(None, title='Webhook Endpoints')
963
979
  webhook_secret: SecretStr | None = Field(None, title='Webhook Secret')
964
980
  client_secret: str | None = Field(None, title='Client Secret')
@@ -147,7 +147,8 @@ class OAuth(Resource):
147
147
  ``None`` when there is nothing to say, otherwise the one thing to tell the user now.
148
148
  ``issue.destination`` says the user can fix it in the Portal: pass it to
149
149
  ``client.portal.sessions.create()`` when they click. It is ``None`` on delegated tokens
150
- and on ``syncing`` or ``unavailable`` issues, so show a button only when it is set.
150
+ and on ``syncing`` issues, so show a button only when it is set. Issues are reported only
151
+ for the capabilities your app declared it uses.
151
152
  Without the ``profile`` scope, ``client.profile.status()`` gives the same ``issue``.
152
153
 
153
154
  Returns:
@@ -24,16 +24,20 @@ class Profile(Resource):
24
24
 
25
25
  **Beta**: the server documents this endpoint as beta.
26
26
 
27
- ``issue`` is ``None`` when there is nothing to tell the user, otherwise
28
- ``{code, status, message, destination}``. Branch on ``issue.status``
29
- (:class:`CapabilityStatus`: ``ready``, ``syncing``, ``action_required``,
30
- ``unavailable``), show ``issue.message``, and show a button only when
31
- ``issue.destination`` is set: on click, pass it to ``client.portal.sessions.create()``.
32
- Don't parse ``message`` or branch on ``code``.
27
+ ``issue`` is ``None`` when nothing is wrong with the data your app uses, otherwise
28
+ ``{code, status, message, destination}``. An issue is always temporary: ``issue.status``
29
+ is ``action_required`` (the user can fix it) or ``syncing`` (the data is arriving), never
30
+ a third value. Show ``issue.message``, and show a button only when ``issue.destination``
31
+ is set: on click, pass it to ``client.portal.sessions.create()``. Don't parse
32
+ ``message`` or branch on ``code``. Issues are reported only for the capabilities your app
33
+ declared it uses (its settings; the default is activities and activity history).
33
34
 
34
35
  ``capabilities`` maps each :class:`Capability` (``activities``, ``activity_history``,
35
- ``dailies``, ``workouts``) to a :class:`CapabilityStatus`. Codes and capability keys are
36
- open sets: a value the client doesn't know parses as a pseudo-member; ignore it.
36
+ ``dailies``, ``workouts``) to a :class:`CapabilityStatus` (``ready``, ``syncing``,
37
+ ``action_required``, ``unavailable``): facts about the user's connected sources, the
38
+ same for every app. ``unavailable`` means no connected source provides it, which
39
+ changes when the user connects one that does. Codes and capability keys are open sets:
40
+ a value the client doesn't know parses as a pseudo-member; ignore it.
37
41
 
38
42
  Accepts a token with ``data:read`` or ``profile``. Delegated tokens are allowed and
39
43
  always get ``destination=None``.
@@ -30,6 +30,7 @@ from .openapi_schemas import (
30
30
  CapabilityStatus,
31
31
  DailyMeasure,
32
32
  DailyResponse,
33
+ IssueStatus,
33
34
  Marker,
34
35
  Metric,
35
36
  PortalDestination,
@@ -1,8 +1,9 @@
1
1
  """Account status: ``userinfo.issue`` and ``GET /api/v1/profile/status``.
2
2
 
3
- The fixtures are the five payloads the public docs prescribe for mocking
3
+ The fixtures are the six payloads the public docs prescribe for mocking
4
4
  ("Why does this user have no data?"), verbatim, so these tests pin the SDK to
5
- the documented contract rather than to our reading of it.
5
+ the documented contract rather than to our reading of it. An issue is always
6
+ temporary: ``action_required`` or ``syncing``, never ``unavailable`` (plan 075).
6
7
  """
7
8
 
8
9
  from unittest.mock import MagicMock, patch
@@ -16,6 +17,7 @@ from sweatstack import (
16
17
  AccountStatusResponse,
17
18
  Capability,
18
19
  CapabilityStatus,
20
+ IssueStatus,
19
21
  PortalDestination,
20
22
  StatusIssueCode,
21
23
  StatusIssueResponse,
@@ -50,12 +52,20 @@ DOCS_PAYLOADS = {
50
52
  "destination": "manage-integrations",
51
53
  }
52
54
  },
53
- "permanently_limited": {
55
+ "blocked_upstream": {
54
56
  "issue": {
55
- "code": "dailies_unavailable",
56
- "status": "unavailable",
57
+ "code": "activities_blocked_upstream",
58
+ "status": "action_required",
59
+ "message": "Intervals.icu can't pass on activities that come from Strava.",
60
+ "destination": "manage-integrations",
61
+ }
62
+ },
63
+ "no_source": {
64
+ "issue": {
65
+ "code": "dailies_no_source",
66
+ "status": "action_required",
57
67
  "message": "No connected source provides daily health data.",
58
- "destination": None,
68
+ "destination": "manage-integrations",
59
69
  }
60
70
  },
61
71
  "all_good": {"issue": None},
@@ -108,7 +118,7 @@ class TestDocumentedPayloads:
108
118
  assert user.issue is None
109
119
  else:
110
120
  assert isinstance(user.issue, StatusIssueResponse)
111
- assert user.issue.status == CapabilityStatus(payload["issue"]["status"])
121
+ assert user.issue.status == IssueStatus(payload["issue"]["status"])
112
122
  assert user.issue.code == StatusIssueCode(payload["issue"]["code"])
113
123
  assert user.issue.message == payload["issue"]["message"]
114
124
  expected = payload["issue"]["destination"]
@@ -2748,7 +2748,7 @@ wheels = [
2748
2748
 
2749
2749
  [[package]]
2750
2750
  name = "sweatstack"
2751
- version = "0.92.0"
2751
+ version = "0.93.0"
2752
2752
  source = { editable = "." }
2753
2753
  dependencies = [
2754
2754
  { name = "email-validator" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes