Skip to content

refactor(llc)!: route user updates through the generated client - #3052

Draft
VelikovPetar wants to merge 4 commits into
refactor/flu-937_user_blocking_openapi_migrationfrom
refactor/flu-953_user_updates_openapi_migration
Draft

VelikovPetar wants to merge 4 commits into
refactor/flu-937_user_blocking_openapi_migrationfrom
refactor/flu-953_user_updates_openapi_migration

Conversation

@VelikovPetar

Copy link
Copy Markdown
Contributor

Submit a pull request

Linear: FLU-953
Github Issue: -

CLA

  • I have signed the Stream CLA (required).
  • The code changes follow best practices
  • Code changes are tested (add some information if not applicable)

Description of the pull request

Moves creating, updating and partially updating users onto the OpenAPI-generated client, as plan group 20 (User Updates), split out of group 09. Stacked on #3050.

  • StreamChatClient.updateUser and updateUsers return Result<UpdateUsersResponse> instead of throwing.
  • partialUpdateUser and partialUpdateUsers are renamed updateUserPartial and updateUsersPartial, and return Result<UpdateUsersResponse>; PartialUpdateUserRequest is renamed UpdateUserPartialRequest (freezed, no toJson).
  • UpdateUsersResponse moves to models/response/ as a freezed class with a non-null duration.
  • User (and OwnUser) gain deactivatedAt, deletedAt and shadowBanned as getters over extraData; every generated user mapper fills the ones its response carries.
  • updateUser sends only the user's name, image, language, visibility and custom data.
  • UsersRepository gains the two operations; user_mapper.dart gains FullUserResponseMapper, UpdateUsersResponseMapper and UpdateUserPartialRequestMapper. The two methods are removed from StreamChatApi.user.
Generated Public
DefaultApi.updateUsers (POST /api/v2/users) StreamChatClient.updateUser, updateUsers
DefaultApi.updateUsersPartial (PATCH /api/v2/users) StreamChatClient.updateUserPartial, updateUsersPartial
UpdateUsersResponse UpdateUsersResponse (ours, freezed)
FullUserResponse User
UpdateUserPartialRequest UpdateUserPartialRequest (ours, freezed; was PartialUpdateUserRequest)
UserRequest built from User by the existing toRequest()

Why

The migration moves every endpoint onto the generated client, which returns a Result for every call. Users decoded from the generated client were also losing deactivatedAt, deletedAt and shadowBanned, which v1 kept raw in extraData; the getters restore them.

Notes for reviewers

Important

FullUserResponse maps to User; there is no public full-user type. Please weigh in. The generated response answers a FullUserResponse per user: the profile plus the fields only that user can see (devices, mutes, channel mutes, privacy settings, unread counts, blocked user ids, hidden channels, token revocation time). UpdateUsersResponse keeps v10's Map<String, User> and maps each entry to a User, dropping that private part. A client-side token can only update its own user (the backend refuses anyone else, even for a client-side admin) and blanks the private part for other users, so a FullUser type would hold empty fields for others and duplicate OwnUser for the caller, whose private fields are on currentUser anyway. queryUsers (group 09) answers the same type and reuses this mapper. The trade-off: v10 callers could read those fields raw from extraData; now they can't. Mapping the caller's own entry to an OwnUser stays an additive option once Mute, ChannelMute and PrivacySettings have response mappers.

Other decision points:

  • User field promotion, the Member pattern: constructor arguments stored in extraData, read back through typed getters, with no persistence change. Whether to turn them into real fields is left to group 09, alongside group 11's ChannelModel and Member promotions. revokeTokensIssuedBefore is deliberately left out.
  • Partial updates take the API's names, matching updateChannelPartial and updateMemberPartial. Hard renames, no deprecated aliases.
  • updateUser no longer sends role, teams or teamsRole. A client-side token never stored them, and a different role was refused with a 403; such a call now succeeds without changing the role.
  • No client-state side effect, as in v10; the user.updated event keeps currentUser in sync.

Backend:

  • Same handlers. POST/PATCH /users and /api/v2/users are UpdateUsers and UpdateUsersPartial from the common routes, mounted twice; only the JSON encoding differs. Not feature-flagged, in beta or deprecated.
  • Fixes a v10 leak: the flattened v1 body stored the client's state (online, banned, timestamps, devices, unread counts, push preferences) as custom data on every upsert, as a v2 read shows. v2 stores only the custom it is given, and toRequest() now also leaves out the own-user keys an OwnUser carries from the connection.
  • The generated request's explicit language: null and invisible: null behave like v10's omitted keys.
  • Known follow-up, recorded as a group 09 risk: ConnectUserDetails still flattens extraData into the WebSocket connect payload; it gets stripped when the WebSocket moves to v2.

Testing

  • melos run analyze clean; stream_chat suite green (1,917 tests); stream_chat_persistence and stream_chat_flutter_core green. generate_plan.py --check reports 0 problems.
  • New client_update_users_test.dart and client_update_users_partial_test.dart:
    • the exact generated request;
    • a fully populated FullUserResponse mapped to the envelope;
    • own-user keys left out of the request, and custom keys named like user fields dropped from the response;
    • batches;
    • each method's failure without throwing.
  • client_connect_guest_user_test.dart and user_test.dart are extended for the promoted fields.
  • Live v1 vs v2 parity on the demo app, on one demo user, restored afterwards:
    • an upsert stores no client state on v2;
    • language survives every write;
    • a partial set and unset with the generated explicit nulls work;
    • the real v2 bodies decode, including another user's blanked fields.

Screenshots / Videos

Not applicable: no visible UI change.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

VelikovPetar and others added 3 commits October 9, 2026 14:27
…refactor/flu-953_user_updates_openapi_migration
…refactor/flu-953_user_updates_openapi_migration
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant