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.
- {sweatstack-0.92.0 → sweatstack-0.93.0}/CHANGELOG.md +18 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/PKG-INFO +1 -1
- {sweatstack-0.92.0 → sweatstack-0.93.0}/pyproject.toml +1 -1
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/client.py +2 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/openapi_schemas.py +41 -25
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/oauth.py +2 -1
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/profile.py +12 -8
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/schemas.py +1 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_account_status.py +17 -7
- {sweatstack-0.92.0 → sweatstack-0.93.0}/uv.lock +1 -1
- {sweatstack-0.92.0 → sweatstack-0.93.0}/.git-blame-ignore-revs +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/.github/workflows/ci.yml +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/.gitignore +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/.gitleaks.toml +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/.python-version +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/AGENTS.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/CONTRIBUTING.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/DEVELOPMENT.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/LICENSE +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/Makefile +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/README.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/evals/__init__.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/evals/agent/__init__.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/evals/agent/agent.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/evals/agent/cases.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/evals/agent/evaluators.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/evals/agent/harness.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/evals/agent/lifecycle.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/evals/agent/run.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/examples/fastapi_webhooks_example.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/examples/send_webhook.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/001a_tests.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/001b_metadata.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/001c_dailies.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/002_TYPED_EXCEPTIONS.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/003_trace_test_linking.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/004_codebase_hygiene.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/005_ost_sport_bridge.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/006_output_backends.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/007_account_status_userinfo_portal.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/008_sdk_documentation.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/plans/009_road_to_1_0.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/skills/sweatstack-python/SKILL.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/skills/sweatstack-python/api.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/skills/sweatstack-python/data-models.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/skills/sweatstack-python/fastapi.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/skills/sweatstack-python/streamlit.md +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/__init__.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/_docs.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/_frames.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/_renames.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/_transport.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/cli.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/constants.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/exceptions.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/fastapi/__init__.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/fastapi/access_token_cache.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/fastapi/config.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/fastapi/dependencies.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/fastapi/models.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/fastapi/routes.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/fastapi/session.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/fastapi/token_stores.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/fastapi/webhooks.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/py.typed +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/__init__.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/_app_metadata.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/_base.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/activities.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/dailies.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/portal.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/teams.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/tests.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/traces.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/resources/users.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/streamlit.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/src/sweatstack/utils.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/__init__.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_access_token_cache.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_dailies.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_docs_check.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_docstrings.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_dtype_conversion.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_exceptions.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_frames.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_identity.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_longitudinal_mean_max_after.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_metadata.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_output.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_portal.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_public_hygiene.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_public_surface.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_renames.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_resources.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_segmentation.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_skill.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_sport_ost.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_teams.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_tests.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_trace_test_linking.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_transport.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_webhooks.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_wire_names.py +0 -0
- {sweatstack-0.92.0 → sweatstack-0.93.0}/tests/test_write_timezone_validation.py +0 -0
- {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
|
|
@@ -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-
|
|
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:
|
|
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``
|
|
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
|
|
28
|
-
``{code, status, message, destination}``.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
36
|
-
|
|
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``.
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"""Account status: ``userinfo.issue`` and ``GET /api/v1/profile/status``.
|
|
2
2
|
|
|
3
|
-
The fixtures are the
|
|
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
|
-
"
|
|
55
|
+
"blocked_upstream": {
|
|
54
56
|
"issue": {
|
|
55
|
-
"code": "
|
|
56
|
-
"status": "
|
|
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":
|
|
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 ==
|
|
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"]
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|