mattermost-mobile-upstream/docs/database/server/server.md
Guillermo Vayá b541867fbf
Add BoardView, PropertyField, PropertyValue WatermelonDB tables (#9708)
* Add BoardView, PropertyField, PropertyValue WatermelonDB tables

Data-layer groundwork for the Integrated Boards feature. Bumps the
server schema from v19 to v20 and adds three new tables with their
models, transformers, handler mixin, and tests. No queries, actions,
or UI yet — those arrive with follow-up work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Align boards types with server contract (BO/BP, KanbanProps)

Widen ChannelType to include BO/BP, add OPEN_BOARD/PRIVATE_BOARD and
CHANNEL_PROPS.BOARD_LINKED_PROPERTIES constants, and replace the
free-form View.props with the typed KanbanProps/KanbanGroupBy/
KanbanColumn shape from mattermost/mattermost#35887.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Allow null View.props to match transformer behavior

The boards transformer assigns null to view.props when neither the
incoming record nor existing row provides one, but the View type
declared props as non-nullable. Widen to KanbanProps | null so
consumers don't need unsafe non-null assertions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* remove test that don't provide value

* Throw synchronously in boards transformers for consistency

Other server transformers (channel, category, post, custom_profile)
throw synchronously from their non-async Promise-returning signatures
when a non-create action is called without an existing record. The
boards transformer was the odd one out using Promise.reject; align
it with the prevailing pattern and update the two tests to assert the
synchronous throw via expect(() => ...).toThrow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Allow null View.description to match transformer behavior

The schema marks description as isOptional, the model types it as
string | null, and the transformer assigns null when the raw payload
omits it. The wire type was string | undefined only — widen to
string | null | undefined so callers handle the null the transformer
can write through.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Make View.props optional to match nullable schema contract

Schema marks props as isOptional and model types it as KanbanProps | null,
so ViewPatch consumers should be able to omit or clear it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Rename View → BoardView across the boards data layer

Avoids the symbol clash with React Native's View component and gives
the migration/handler names a clearer boards-specific identity.

Renames:
- API type View → BoardView, ViewPatch → BoardViewPatch
- Model class ViewModel → BoardViewModel
- MM_TABLES.SERVER.VIEW → BOARD_VIEW (table name string unchanged)
- transformViewRecord → transformBoardViewRecord
- handleViews → handleBoardViews (views arg → boardViews)
- HandleViewsArgs → HandleBoardViewsArgs

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Extract PropertyField/Value transformers and handlers into properties.ts

PropertyField and PropertyValue are shared primitives, not boards-specific
(future features such as cards and content flagging will reuse them).
Moving them out of the boards files keeps the boards namespace focused on
BoardView and lets other product features import the property handlers
without a boards dependency.

- transformers/properties.ts: transformPropertyFieldRecord, transformPropertyValueRecord
- handlers/properties.ts: PropertiesHandler mixin + PropertiesHandlerMix
- boards.ts files keep only BoardView code
- ServerDataOperator now composes PropertiesHandler alongside BoardsHandler
- Tests split into properties.test.ts and boards.test.ts accordingly

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Move boards code under app/products/boards (mirroring playbooks layout)

BoardView is a boards-specific feature, so its model, schema, transformer,
and handler should live alongside the rest of the product code rather than
in the top-level database namespace. Adopt the same layout used by
playbooks and agents.

Structure:
  app/products/boards/
    constants/database.ts                       (BOARDS_TABLES)
    database/
      models/{index.ts,board_view.ts}
      schema/{index.ts,board_view.ts}
      operators/
        transformers/{index.ts,index.test.ts}
        handlers/{index.ts,index.test.ts}
    types/
      api.d.ts                                   (ambient BoardView/KanbanProps/...)
      database/models/board_view.ts              (BoardViewModel interface)

Wiring:
- tsconfig.json + babel.config.js: add @boards/* alias
- DatabaseManager (+ mock): import BoardViewModel from @boards
- Server schema index: import BoardViewSchema from @boards
- ServerDataOperator: import BoardsHandler from @boards
- Migration: import BOARD_VIEW from @boards/constants/database
- MM_TABLES.SERVER no longer carries BOARD_VIEW
- HandleBoardViewsArgs moved into the boards handler module
  (matches the playbooks pattern of co-locating handler args)

PropertyField/PropertyValue remain at the top-level operator because they
are shared infrastructure not tied to boards.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* update docs

* fix(boards): register board database imports

* test(boards): isolate handler database lifecycle

* test(properties): isolate handler database lifecycle

* snapshot

* fix snapshots back

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 12:46:40 +02:00

505 lines
11 KiB
Markdown

# Server Database - Schema Version 20
# Please bump the version by 1, any time the schema changes.
# Also, include the migration plan under app/database/migration/server,
# update all models, relationships and types.
# Lastly, export all PNGs, SVGs, etc under the source project (./docs/database)
# If you have any question/queries that you would like to clarify, please reach out to the Mobile Platform Team.
Category
-
id PK string # server-generated
collapsed bool
display_name string
muted bool
sort_order number
sorting string # alpha, recent, manual
team_id string INDEX FK >- Team.id
type string # 'channels' | 'direct_messages' | 'favorites' | 'custom'
CategoryChannel
-
id PK string # composition ID Team.id_Channel.id
category_id string INDEX FK >- Category.id
channel_id string INDEX
sort_order number
Channel
-
id PK string FK - CategoryChannel.channel_id # server-generated
create_at string
creator_id string INDEX FK >- User.id
delete_at number
display_name string
is_group_constrained bool
name string INDEX
shared bool
team_id string INDEX FK >- Team.id
type string
update_at number
banner_info string
abac_policy_enforced boolean
autotranslation boolean
ChannelInfo
-
id PK string FK - Channel.id# same value as Channel.id
guest_count number
header string
member_count number
pinned_post_count number
files_count number
purpose string
ChannelMembership
-
id PK string # composition ID Channel.id-User.id
channel_id string INDEX FK >- Channel.id
user_id string INDEX FK >- User.id
scheme_admin bool
CustomEmoji
-
id PK string # auto-generated
name string
CustomProfileField
-
id PK string # server-generated
group_id string
name string
type string
target_id string
target_type string
create_at number
update_at number
delete_at number
attrs string NULL # stringified JSON
CustomProfileAttribute
-
id PK string # composition ID User.id-CustomProfileField.id
field_id string INDEX FK >- CustomProfileField.id
user_id string INDEX FK >- User.id
value string
Draft
-
id PK string # auto-generated
channel_id string INDEX FK >- Channel.id
files string #stringify (array)
message string
root_id string INDEX NULL FK >- Post.id
type string NULL
ScheduledPost
-
id PK string # auto-generated
channel_id string INDEX FK >- Channel.id
files string #stringify (array)
message string
root_id string INDEX NULL FK >- Post.id
metadata string NULL
create_at number
update_at number
scheduled_at number
processed_at number NULL
error_code string
type string NULL
File
-
id PK string # server-generated
extension string
height number
image_thumbnail string #base64 data string or filepath for video thumbnails
local_path string NULL
mime_type string
name string
post_id string INDEX FK >- Post.id
size number
width number
is_blocked bool #Determines if a file has been blocked and cannot be opened anymore
Group
-
id PK string # server-generated
name string INDEX
display_name string
description string
remote_id string INDEX
source string
created_at number
updated_at number
deleted_at number
GroupChannel
-
id PK string # composition ID Group.id-Channel.id
group_id string INDEX FK >- Group.id
channel_id string INDEX FK >- Channel.id
created_at number
updated_at number
deleted_at number
GroupMembership
-
id PK string # composition ID Group.id-User.id
group_id string INDEX FK >- Group.id
user_id string INDEX FK >- User.id
created_at number
updated_at number
deleted_at number
GroupTeam
-
id PK string # composition ID Group.id-Team.id
group_id string INDEX FK >- Group.id
team_id string INDEX FK >- Team.id
created_at number
updated_at number
deleted_at number
MyChannel
-
id PK string FK - Channel.id # same as Channel.id
is_unread boolean
last_post_at number
last_viewed_at number
manually_unread boolean
mentions_count number
message_count number
roles string
viewed_at number
autotranslation_disabled boolean
MyChannelSettings
-
id PK string FK - MyChannel.id # same as Channel.id
notify_props string
MyTeam
-
id PK string FK - Team.id # same as Team.id
roles string
PlaybookRun
-
id PK string # server-generated
playbook_id string
name string
description string
is_active boolean
active_stage number
active_stage_title string
participant_ids string # stringified array of user IDs
summary string
current_status string # (valid values InProgres, Finished)
owner_user_id string INDEX FK >- User.id
team_id string INDEX FK >- Team.id
channel_id string INDEX FK >- Channel.id
post_id string INDEX FK >- Post.id
create_at number
end_at number
delete_at number
last_status_update_at number
retrospective_enabled boolean
retrospective string
retrospective_published_at number
synced string NULL INDEX # optional field for sync status
last_sync_at number NULL # optional field for last sync timestamp
PlaybookChecklist
-
id PK string # server-generated
run_id string INDEX FK >- PlaybookRun.id
title string
sort_order number
synced string NULL INDEX # optional field for sync status
last_sync_at number NULL # optional field for last sync timestamp
PlaybookChecklistItem
-
id PK string # server-generated
checklist_id string INDEX FK >- PlaybookChecklist.id
title string
description string
state string # e.g., 'open', 'closed'
state_modified number
assignee_id string INDEX FK >- User.id
assignee_modified number
command string
command_last_run number
due_date number
task_actions string # stringified array of TaskAction
condition_action string # condition action for the checklist item (values should be '' or 'hidden')
condition_reason string # condition reason for the checklist item (default empty string meaning there is no condition affecting)
order number
completed_at number
synced string NULL INDEX # optional field for sync status
last_sync_at number NULL # optional field for last sync timestamp
PlaybookRunAttribute
-
id PK string # server-generated
group_id string
name string
type string
target_id string
target_type string
create_at number
update_at number
delete_at number
attrs string NULL # stringified JSON
PlaybookRunAttributeValue
-
id PK string # composition ID PlaybookRun.id-PlaybookRunAttribute.id
attribute_id string INDEX FK >- PlaybookRunAttribute.id
run_id string INDEX FK >- PlaybookRun.id
value string
Post
-
id PK string # server generated
channel_id string INDEX FK >- Channel.id
create_at number
delete_at number
edit_at number
is_pinned boolean
message string
metadata string NULL
original_id string
pending_post_id string INDEX
previous_post_id string
props string
root_id string
type string INDEX
update_at number
user_id string INDEX FK >- User.id
PostsInChannel
-
id PK string # auto-generated
channel_id string INDEX FK >- Channel.id
earliest number
latest number
PostsInThread
-
id PK string # auto-generated
earliest number
latest number
root_id string FK >- Post.id
Preference
-
id string PK # server-generated
category string INDEX
name string
user_id string INDEX FK >- User.id
value string
Reaction
-
id PK string # server-generated
create_at number
emoji_name string
post_id string INDEX FK >- Post.id
user_id string INDEX FK >- User.id
Role
-
id PK string # server-generated
name string
permissions string #stringify array
System
-
id PK string # SYSTEM_IDENTIFIERS
value string
Team
-
id PK string # server-generated
allowed_domains string
description string
display_name string
is_allow_open_invite boolean
is_group_constrained boolean
last_team_icon_updated_at number
name string
type string
update_at number
TeamChannelHistory
-
id PK string FK - Team.id # same as Team.id
channel_ids string # stringified JSON array; FIFO
TeamMembership
-
id PK string # auto-generated
team_id string INDEX FK >- Team.id
user_id string INDEX FK >- User.id
scheme_admin bool
TeamSearchHistory
-
id PK string # auto-generated
created_at number
display_term string
team_id string FK >- Team.id
term string
Thread
-
id PK string FK - Post.id # similar to Post.id but for root post only
is_following boolean
last_reply_at number
last_viewed_at number
reply_count number
unread_mentions number
unread_replies number
viewed_at number
ThreadsInTeam
-
id PK string # auto-generated
loaded_in_global_threads boolean
team_id string INDEX FK >- Team.id
thread_id string INDEX FK >- Thread.id
ThreadParticipant # who is participating in this conversation
-
id PK string # auto-generated
thread_id string INDEX FK >- Thread.id
user_id string INDEX FK >- User.id
User
-
id PK string # server generated
auth_service string
delete_at number
email string
first_name string
is_bot boolean
is_guest boolean
last_name string
last_picture_update number
locale string
nickname string
notify_props string
position string
props string
remote_id string NULL
roles string
status string
timezone string
update_at number
username string
AiBot
-
id PK string
display_name string
username string
last_icon_update number
dm_channel_id string INDEX
channel_access_level number
channel_ids string
user_access_level number
user_ids string
team_ids string
AiThread
-
id PK string
message string
title string
channel_id string INDEX FK >- Channel.id
reply_count number
update_at number INDEX
BoardView
-
# WatermelonDB table name is BoardView; the domain model class is BoardViewModel.
# "BoardView" avoids collision with SQL's reserved VIEW keyword (and with React Native's View) and encodes the boards semantics.
id PK string # server-generated
channel_id string INDEX FK >- Channel.id
type string # 'kanban'
creator_id string FK >- User.id
title string
description string
sort_order number
props string # JSON
create_at number
update_at number INDEX
delete_at number # soft-delete, 0 when active
PropertyField
-
# Definition of a card (or other object) property. Shared infrastructure reused by future property-based features.
id PK string # server-generated
group_id string # opaque; resolved server-side
name string
type string # 'text' | 'select' | 'multiselect' | ...
attrs string # JSON (options, sort_order, ...)
object_type string # 'card' | 'post' | 'user'
target_id string INDEX # scope target (channel/team id, empty for system)
target_type string # 'system' | 'team' | 'channel'
protected boolean
permission_field string
permission_values string # JSON
permission_options string # JSON
create_at number
update_at number INDEX
delete_at number # soft-delete, 0 when active
created_by string FK >- User.id
updated_by string FK >- User.id
PropertyValue
-
# Single object's value for a single PropertyField.
# target_id is polymorphic (keyed by target_type); no typed WatermelonDB relation is declared for it.
id PK string # server-generated
field_id string INDEX FK >- PropertyField.id
target_id string INDEX # polymorphic target
target_type string # 'post' | 'user' | ...
group_id string
value string # JSON (shape depends on field type)
create_at number
update_at number
delete_at number # soft-delete, 0 when active
created_by string FK >- User.id
updated_by string FK >- User.id