Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions openapi-migration/04-roles-guest-and-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,11 @@
in the raw `me`. None was ever applied. Verified live against the demo app and in the backend source;
recorded as a 🐞 Fixed entry.
- **`UserResponse.toModel()` leaves custom keys named like `OwnUser` fields (`OwnUser.topLevelFields`),
plus `deleted_at`, `deactivated_at` and `revoke_tokens_issued_before`, out of `extraData`.** v1's flat
plus `deleted_at` and `deactivated_at`, out of `extraData`.** v1's flat
user JSON shadowed them; v2 nests custom data, and without the guard a custom `online` string crashed
`OwnUser.fromUser`. `User.toRequest()` leaves the same keys out of the custom data it sends.
- **The three extra names are guarded because the socket connect refuses them.** A custom
`deleted_at`, `deactivated_at` or `revoke_tokens_issued_before` left in `extraData` is re-sent in the
- **The two extra names are guarded because the socket connect refuses them.** A custom
`deleted_at` or `deactivated_at` left in `extraData` is re-sent in the
connect's user details, and the backend answers 400 ("reserved field", or "expected date" for a
non-date value). v10's flat response shadowed them, so the same guest connected; confirmed live.
- **Not a regression: a custom `ban_expires` that isn't a date** fails the connect in v10 and now alike,
Expand Down
4 changes: 2 additions & 2 deletions openapi-migration/09-users.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

- `User` and `OwnUser` are public, persisted, and embedded in nearly every other response. This group restructures them, last: the mappers in `user_mapper.dart` already map the generated types onto the current class for every group before it, and stay. See [01-foundation](01-foundation.md).
- `PrivacySettings` and the push-preference sub-shapes — decide per type.
- **`User.name` and `image` become fields** (README rule 9): `UserResponse` types them, and the constructor stops writing them into `extraData`; decide whether `name` keeps its fallback to `id`. `deleted_at`, `deactivated_at`, `revoke_tokens_issued_before` and `blocked_user_ids`, which `UserResponse` types and v1 kept in a plain user's `extraData`, become fields or are dropped, never `extraData`. `UserResponse.toModel()` then copies `custom` verbatim into the field, renamed `custom`, and `UserRequestMapper` stops reading `extraData['name']` and `['image']`.
- **`User.name` and `image` become fields** (README rule 9): `UserResponse` types them, and the constructor stops writing them into `extraData`; decide whether `name` keeps its fallback to `id`. `deleted_at`, `deactivated_at` and `blocked_user_ids`, which `UserResponse` types and v1 kept in a plain user's `extraData`, become fields or are dropped, never `extraData`. `UserResponse.toModel()` then copies `custom` verbatim into the field, renamed `custom`, and `UserRequestMapper` stops reading `extraData['name']` and `['image']`.
- `UserFilterField.shadowBanned` and `.bypassModeration` read `extraData`, which the generated `UserResponse` has no field to fill.
- `updateUsers` reuses `User.toRequest()`. It is a full upsert, and v10's flattened body filed the user's client state (`online`, `banned`, `created_at` and similar) into the stored custom data on every call, as it did for guests in [04](04-roles-guest-and-app.md). Decide the same way here, for real users rather than fresh guests, and record it in the CHANGELOG.
- The generated `UserRequest` sends explicit `null` for an unset `language` or `invisible`, where v10 left the key out. For a guest create that made no difference; for an upsert of an existing user, confirm live that a `null` does not reset a stored value differently from an omitted key.
Expand All @@ -51,7 +51,7 @@
- Every other group depends on the `User` decision.
- User data arrives over the WebSocket on nearly every event.
- Landing `UserResponse` -> `User` unblocks the two fields [08](08-moderation-and-blocklists.md) had to drop from `MuteUsersResponse`: the `mutes` the call created and the `ownUser` it left behind. Adding them is additive for anyone reading the response, so revisit them here rather than leaving them dropped for good.
- Until then, a user mapped from `UserResponse` and the same user decoded from v1 JSON carry different `extraData`: the v1 path keeps `blocked_user_ids`, `deleted_at`, `deactivated_at` and `revoke_tokens_issued_before`, the mapper drops them. Equality includes `extraData`, so a `Poll` from a REST call (its `createdBy`, its votes' `user`) and the same poll from an event compare unequal.
- Until then, a user mapped from `UserResponse` and the same user decoded from v1 JSON carry different `extraData`: the v1 path keeps `blocked_user_ids`, `deleted_at` and `deactivated_at`, the mapper drops them. Equality includes `extraData`, so a `Poll` from a REST call (its `createdBy`, its votes' `user`) and the same poll from an event compare unequal.

## Definition of done

Expand Down
3 changes: 2 additions & 1 deletion openapi-migration/11-channels-and-members.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**Goal:** The biggest group, and the one every controller above it reads through `ChannelState`.

**Size:** 27 hand-written method(s) across 2 file(s) → 24 generated operation(s).
**Size:** 27 hand-written method(s) across 2 file(s) → 25 generated operation(s).

> This whole file is generated by `openapi-migration/tool/generate_plan.py`, prose included.
> Edit its `GROUPS` entry and re-run the script — edits made here are lost on the next run.
Expand Down Expand Up @@ -58,6 +58,7 @@
| `POST` | `/api/v2/chat/channels/delivered` | `markDelivered` | `MarkDeliveredResponse` |
| `POST` | `/api/v2/chat/channels/{type}/{id}/read` | `markRead` | `MarkReadResponse` |
| `POST` | `/api/v2/chat/channels/{type}/{id}/unread` | `markUnread` | `DurationResponse` |
| `POST` | `/api/v2/chat/channels/{type}/{id}/bans/query` | `queryChannelBans` | `QueryChannelBansResponse` |
| `POST` | `/api/v2/chat/channels` | `queryChannels` | `QueryChannelsResponse` |
| `GET` | `/api/v2/chat/members` | `queryMembers` | `MembersResponse` |
| `POST` | `/api/v2/chat/channels/{type}/{id}/event` | `sendEvent` | `EventResponse` |
Expand Down
31 changes: 19 additions & 12 deletions openapi-migration/tool/generate_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,18 @@ def read_handwritten():


def read_generated():
"""[(verb, path, operation, responseType)] for the generated client."""
return re.findall(
r"@(GET|POST|PATCH|PUT|DELETE)\('([^']+)'\)\s*\n\s*Future<Result<(\w+)>>\s+(\w+)\(",
GENERATED_API.read_text(),
)
"""[(verb, path, operation, responseType)] for the generated client.

An operation that sends a query parameter as JSON is annotated on a private `_name` method, which the public
`name` forwards to; it is listed under the public name.
"""
return [
(verb, path, response, operation.removeprefix('_'))
for verb, path, response, operation in re.findall(
r"@(GET|POST|PATCH|PUT|DELETE)\('([^']+)'\)\s*\n\s*Future<Result<(\w+)>>\s+(\w+)\(",
GENERATED_API.read_text(),
)
]


def owns(*prefixes, unless=()):
Expand Down Expand Up @@ -289,11 +296,11 @@ def match(path):
in the raw `me`. None was ever applied. Verified live against the demo app and in the backend source;
recorded as a 🐞 Fixed entry.
- **`UserResponse.toModel()` leaves custom keys named like `OwnUser` fields (`OwnUser.topLevelFields`),
plus `deleted_at`, `deactivated_at` and `revoke_tokens_issued_before`, out of `extraData`.** v1's flat
plus `deleted_at` and `deactivated_at`, out of `extraData`.** v1's flat
user JSON shadowed them; v2 nests custom data, and without the guard a custom `online` string crashed
`OwnUser.fromUser`. `User.toRequest()` leaves the same keys out of the custom data it sends.
- **The three extra names are guarded because the socket connect refuses them.** A custom
`deleted_at`, `deactivated_at` or `revoke_tokens_issued_before` left in `extraData` is re-sent in the
- **The two extra names are guarded because the socket connect refuses them.** A custom
`deleted_at` or `deactivated_at` left in `extraData` is re-sent in the
connect's user details, and the backend answers 400 ("reserved field", or "expected date" for a
non-date value). v10's flat response shadowed them, so the same guest connected; confirmed live.
- **Not a regression: a custom `ban_expires` that isn't a date** fails the connect in v10 and now alike,
Expand Down Expand Up @@ -574,8 +581,8 @@ def match(path):
'`PrivacySettings` and the push-preference sub-shapes — decide per type.',
'**`User.name` and `image` become fields** (README rule 9): `UserResponse` types them, and the constructor '
'stops writing them into `extraData`; decide whether `name` keeps its fallback to `id`. `deleted_at`, '
'`deactivated_at`, `revoke_tokens_issued_before` and `blocked_user_ids`, which `UserResponse` types and v1 '
'kept in a plain user\'s `extraData`, become fields or are dropped, never `extraData`. '
'`deactivated_at` and `blocked_user_ids`, which `UserResponse` types and v1 kept in a plain user\'s '
'`extraData`, become fields or are dropped, never `extraData`. '
'`UserResponse.toModel()` then copies `custom` verbatim into the field, renamed `custom`, and '
'`UserRequestMapper` stops reading `extraData[\'name\']` and `[\'image\']`.',
'`UserFilterField.shadowBanned` and `.bypassModeration` read `extraData`, which the generated '
Expand All @@ -596,8 +603,8 @@ def match(path):
'behind. Adding them is additive for anyone reading the response, so revisit them here rather '
'than leaving them dropped for good.',
'Until then, a user mapped from `UserResponse` and the same user decoded from v1 JSON carry different '
'`extraData`: the v1 path keeps `blocked_user_ids`, `deleted_at`, `deactivated_at` and '
'`revoke_tokens_issued_before`, the mapper drops them. Equality includes `extraData`, so a `Poll` from a '
'`extraData`: the v1 path keeps `blocked_user_ids`, `deleted_at` and `deactivated_at`, '
'the mapper drops them. Equality includes `extraData`, so a `Poll` from a '
'REST call (its `createdBy`, its votes\' `user`) and the same poll from an event compare unequal.',
],
done=DONE + (
Expand Down
2 changes: 2 additions & 0 deletions packages/stream_chat/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@

- `connectGuestUser` no longer leaves the signed-in user unauthenticated when it refuses to connect a guest over them.

- The OpenAPI-generated client's methods that take a `payload` or `sort` argument no longer fail with `invalid json data`.

🔄 Changed

- Failed messages now retry on server errors. The retry policy is now explicit: retry a request that never reached the server, a 5xx, a 429 and a 408; never another 4xx, a cancelled request, broken credentials, or anything the server marked unrecoverable. Previously only failures without a parseable error body retried, so a 500 or a 429 did not.
Expand Down
8 changes: 4 additions & 4 deletions packages/stream_chat/lib/open_api/api.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Code generated by GetStream internal OpenAPI code generator. DO NOT EDIT.
//
// Spec: chat-clientside-api (API v239.44.1)
// Source: protocol @ openapi-v239.44.1
// Checksum: 6219cc7928894656eb5710263eb971607a124203ff0e4c694f48a61c907e2aca
// Generator: GetStream/chat @ v239.44.1-22-g2a92b14e14
// Spec: chat-clientside-api (API v239.70.0)
// Source: protocol @ openapi-v239.70.0
// Checksum: 4472292a4951e16b2f439517fba42e2248e16e0413590b9b591479959625c1e4
// Generator: GetStream/chat @ v239.67.0-238-g8d6e1fa600

// ignore_for_file: directives_ordering

Expand Down
Loading
Loading