Gekidou - Updated Server Database Diagrams/Schema/Models (#6119)

* started with the diagrams

* removed redundant tables

next step:
1. reconstruct id ( local id vs server id )
2. annotate fields with examples
3. recreate relationship

* work in progress

* work in progress

* fix association

* update postsInChannel

* removed SlashCommands from the Server database schema

* added missing associations in the models and updated docs/database

* exported server database

* update test

* code corrections following review

* update relationship

* update docs

* removed cyclic relationship

* Revert "removed cyclic relationship"

This reverts commit 4d784efb815c0430aa3f7cc433ac4125898d2621.

* removed isOptional from Draft

* linked myChannelSettings to myChannel instead of Channel

* update diagrams

* store null instead of empty string

* update thread association

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
Avinash Lingaloo 2022-04-07 18:14:28 +04:00 committed by GitHub
parent ae3c9e2ef2
commit 9a72837f04
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 987 additions and 95 deletions

View file

@ -48,7 +48,7 @@ export const storeCategories = async (serverUrl: string, categories: CategoryWit
}
const models = await Promise.all(modelPromises);
const flattenedModels = models.flat() as Model[];
const flattenedModels = models.flat();
if (prune && categories.length) {
const {database} = operator;

View file

@ -15,16 +15,26 @@ import ChannelListItem from './channel_list_item';
import type {WithDatabaseArgs} from '@typings/database/database';
const enhance = withObservables(['channel'], ({channel, database}: {channel: ChannelModel} & WithDatabaseArgs) => {
const myChannel = observeMyChannel(database, channel.id);
const currentUserId = observeCurrentUserId(database);
const settings = channel.settings.observe();
const myChannel = observeMyChannel(database, channel.id);
let isMuted = of$(false);
if (myChannel) {
const settings = myChannel.pipe(
switchMap((mc) => {
return mc ? mc.settings.observe() : of$(undefined);
}),
);
isMuted = settings?.pipe(
switchMap((s) => of$(s?.notifyProps?.mark_unread === 'mention')),
);
}
return {
currentUserId,
isMuted: settings.pipe(
switchMap((s) => of$(s?.notifyProps?.mark_unread === 'mention')),
),
isMuted,
myChannel,
channel: channel.observe(),
};

View file

@ -32,10 +32,11 @@ export default class CategoryModel extends Model implements CategoryInterface {
/** associations : Describes every relationship to this table. */
static associations: Associations = {
/** A CATEGORY has a 1:N relationship with CHANNEL. A CATEGORY can possess multiple channels */
/** A CHANNEL can belong to several CATEGORY, and a CATEGORY posses multiple channels (N:N relationship).
* We use the intermediate table CATEGORY_CHANNEL for this relationship */
[CATEGORY_CHANNEL]: {type: 'has_many', foreignKey: 'category_id'},
/** A TEAM can be associated to CATEGORY (relationship is 1:N) */
/** A Category belongs to a Team, and a Team can have several categories (relationship 1:N) */
[TEAM]: {type: 'belongs_to', key: 'team_id'},
};

View file

@ -13,7 +13,6 @@ import type ChannelInfoModel from '@typings/database/models/servers/channel_info
import type ChannelMembershipModel from '@typings/database/models/servers/channel_membership';
import type DraftModel from '@typings/database/models/servers/draft';
import type MyChannelModel from '@typings/database/models/servers/my_channel';
import type MyChannelSettingsModel from '@typings/database/models/servers/my_channel_settings';
import type PostModel from '@typings/database/models/servers/post';
import type PostsInChannelModel from '@typings/database/models/servers/posts_in_channel';
import type TeamModel from '@typings/database/models/servers/team';
@ -26,7 +25,6 @@ const {
CHANNEL_MEMBERSHIP,
DRAFT,
MY_CHANNEL,
MY_CHANNEL_SETTINGS,
POSTS_IN_CHANNEL,
POST,
TEAM,
@ -46,7 +44,7 @@ export default class ChannelModel extends Model implements ChannelModelInterface
/** A CHANNEL can be associated with multiple CHANNEL_MEMBERSHIP (relationship is 1:N) */
[CHANNEL_MEMBERSHIP]: {type: 'has_many', foreignKey: 'channel_id'},
/** A CHANNEL can be associated with multiple CATEGORY_CHANNEL (relationship is 1:N) */
/** A CHANNEL can be associated with one CATEGORY_CHANNEL per team (relationship is 1:1) */
[CATEGORY_CHANNEL]: {type: 'has_many', foreignKey: 'channel_id'},
/** A CHANNEL can be associated with multiple DRAFT (relationship is 1:N) */
@ -66,6 +64,10 @@ export default class ChannelModel extends Model implements ChannelModelInterface
/** A USER can create multiple CHANNEL (relationship is 1:N) */
[USER]: {type: 'belongs_to', key: 'creator_id'},
/** A CHANNEL is associated with one CHANNEL_INFO**/
[CHANNEL_INFO]: {type: 'has_many', foreignKey: 'id'},
};
/** create_at : The creation date for this channel */
@ -126,9 +128,6 @@ export default class ChannelModel extends Model implements ChannelModelInterface
/** membership : Query returning the membership data for the current user if it belongs to this channel */
@immutableRelation(MY_CHANNEL, 'id') membership!: Relation<MyChannelModel>;
/** settings: User specific settings/preferences for this channel */
@immutableRelation(MY_CHANNEL_SETTINGS, 'id') settings!: Relation<MyChannelSettingsModel>;
/** categoryChannel : Query returning the membership data for the current user if it belongs to this channel */
@immutableRelation(CATEGORY_CHANNEL, 'channel_id') categoryChannel!: Relation<CategoryChannelModel>;

View file

@ -6,11 +6,12 @@ import {field, immutableRelation} from '@nozbe/watermelondb/decorators';
import Model, {Associations} from '@nozbe/watermelondb/Model';
import {MM_TABLES} from '@constants/database';
import MyChannelSettingsModel from '@typings/database/models/servers/my_channel_settings';
import type ChannelModel from '@typings/database/models/servers/channel';
import type MyChannelModelInterface from '@typings/database/models/servers/my_channel';
const {CATEGORY_CHANNEL, CHANNEL, MY_CHANNEL} = MM_TABLES.SERVER;
const {CATEGORY_CHANNEL, CHANNEL, MY_CHANNEL, MY_CHANNEL_SETTINGS} = MM_TABLES.SERVER;
/**
* MyChannel is an extension of the Channel model but it lists only the Channels the app's user belongs to
@ -50,4 +51,13 @@ export default class MyChannelModel extends Model implements MyChannelModelInter
/** channel : The relation pointing to the CHANNEL table */
@immutableRelation(CHANNEL, 'id') channel!: Relation<ChannelModel>;
/** settings: User specific settings/preferences for this channel */
@immutableRelation(MY_CHANNEL_SETTINGS, 'id') settings!: Relation<MyChannelSettingsModel>;
async destroyPermanently() {
const settings = await this.settings.fetch();
settings?.destroyPermanently();
super.destroyPermanently();
}
}

View file

@ -3,15 +3,15 @@
import {Relation} from '@nozbe/watermelondb';
import {immutableRelation, json} from '@nozbe/watermelondb/decorators';
import Model from '@nozbe/watermelondb/Model';
import Model, {Associations} from '@nozbe/watermelondb/Model';
import {MM_TABLES} from '@constants/database';
import {safeParseJSON} from '@utils/helpers';
import type ChannelModel from '@typings/database/models/servers/channel';
import type MyChannelModel from '@typings/database/models/servers/my_channel';
import type MyChannelSettingsModelInterface from '@typings/database/models/servers/my_channel_settings';
const {CHANNEL, MY_CHANNEL_SETTINGS} = MM_TABLES.SERVER;
const {MY_CHANNEL, MY_CHANNEL_SETTINGS} = MM_TABLES.SERVER;
/**
* The MyChannelSettings model represents the specific user's configuration to
@ -21,9 +21,15 @@ export default class MyChannelSettingsModel extends Model implements MyChannelSe
/** table (name) : MyChannelSettings */
static table = MY_CHANNEL_SETTINGS;
static associations: Associations = {
/** A MY_CHANNEL is associated with one MY_CHANNEL_SETTINGS (relationship is 1:1) **/
[MY_CHANNEL]: {type: 'belongs_to', key: 'id'},
};
/** notify_props : Configurations in regard to this channel */
@json('notify_props', safeParseJSON) notifyProps!: ChannelNotifyProps;
/** channel : The relation pointing to the CHANNEL table */
@immutableRelation(CHANNEL, 'id') channel!: Relation<ChannelModel>;
/** channel : The relation pointing to the MY_CHANNEL table */
@immutableRelation(MY_CHANNEL, 'id') myChannel!: Relation<MyChannelModel>;
}

View file

@ -20,6 +20,8 @@ export default class MyTeamModel extends Model implements MyTeamModelInterface {
static table = MY_TEAM;
static associations: Associations = {
/** A TEAM is associated to one MY_TEAM (relationship is 1:1) */
[TEAM]: {type: 'belongs_to', key: 'id'},
};

View file

@ -23,7 +23,7 @@ export default class PostsInThreadModel extends Model implements PostsInThreadMo
/** associations : Describes every relationship to this table. */
static associations: Associations = {
/** A POST can have a POSTS_IN_THREAD.(relationship is 1:1)*/
/** A POST can have multiple POSTS_IN_THREAD. (relationship is 1:N)*/
[POST]: {type: 'belongs_to', key: 'root_id'},
};

View file

@ -55,6 +55,9 @@ export default class TeamModel extends Model implements TeamModelInterface {
/** A TEAM has a 1:N relationship with THREADS_IN_TEAM. A TEAM can possess multiple threads */
[THREADS_IN_TEAM]: {type: 'has_many', foreignKey: 'team_id'},
/** A TEAM has a 1:1 relationship with TEAM_CHANNEL_HISTORY. */
[TEAM_CHANNEL_HISTORY]: {type: 'has_many', foreignKey: 'id'},
};
/** is_allow_open_invite : Boolean flag indicating if this team is open to the public */

View file

@ -3,7 +3,7 @@
import {Relation} from '@nozbe/watermelondb';
import {immutableRelation, json} from '@nozbe/watermelondb/decorators';
import Model from '@nozbe/watermelondb/Model';
import Model, {Associations} from '@nozbe/watermelondb/Model';
import {MM_TABLES} from '@constants/database';
import {safeParseJSON} from '@utils/helpers';
@ -21,6 +21,12 @@ export default class TeamChannelHistoryModel extends Model implements TeamChanne
/** table (name) : TeamChannelHistory */
static table = TEAM_CHANNEL_HISTORY;
static associations: Associations = {
/** A TEAM has a 1:1 relationship with TEAM_CHANNEL_HISTORY. */
[TEAM]: {type: 'belongs_to', key: 'id'},
};
/** channel_ids : An array containing the last 5 channels visited within this team order by recency */
@json('channel_ids', safeParseJSON) channelIds!: string[];

View file

@ -1,6 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Query} from '@nozbe/watermelondb';
import {children, field, json} from '@nozbe/watermelondb/decorators';
import Model, {Associations} from '@nozbe/watermelondb/Model';
@ -121,25 +122,25 @@ export default class UserModel extends Model implements UserModelInterface {
@json('timezone', safeParseJSON) timezone!: UserTimezone | null;
/** channelsCreated : All the channels that this user created */
@children(CHANNEL) channelsCreated!: ChannelModel[];
@children(CHANNEL) channelsCreated!: Query<ChannelModel>;
/** channels : All the channels that this user is part of */
@children(CHANNEL_MEMBERSHIP) channels!: ChannelMembershipModel[];
@children(CHANNEL_MEMBERSHIP) channels!: Query<ChannelMembershipModel>;
/** posts : All the posts that this user has written*/
@children(POST) posts!: PostModel[];
@children(POST) posts!: Query<PostModel>;
/** preferences : All user preferences */
@children(PREFERENCE) preferences!: PreferenceModel[];
@children(PREFERENCE) preferences!: Query<PreferenceModel>;
/** reactions : All the reactions to posts that this user had */
@children(REACTION) reactions!: ReactionModel[];
@children(REACTION) reactions!: Query<ReactionModel>;
/** teams : All the team that this user is part of */
@children(TEAM_MEMBERSHIP) teams!: TeamMembershipModel[];
@children(TEAM_MEMBERSHIP) teams!: Query<TeamMembershipModel>;
/** threadParticipations : All the thread participations this user is part of */
@children(THREAD_PARTICIPANT) threadParticipations!: ThreadParticipantsModel[];
@children(THREAD_PARTICIPANT) threadParticipations!: Query<ThreadParticipantsModel>;
prepareStatus = (status: string) => {
this.prepareUpdate((u) => {

View file

@ -72,11 +72,11 @@ const PostHandler = (superclass: any) => class extends superclass {
* handlePosts: Handler responsible for the Create/Update operations occurring on the Post table from the 'Server' schema
* @param {HandlePostsArgs} handlePosts
* @param {string} handlePosts.actionType
* @param {string[]} handlePosts.orders
* @param {RawPost[]} handlePosts.values
* @param {string[]} handlePosts.order
* @param {RawPost[]} handlePosts.posts
* @param {string | undefined} handlePosts.previousPostId
* @param {boolean | undefined} handlePosts.prepareRecordsOnly
* @returns {Promise<void>}
* @returns {Promise<Model[]>}
*/
handlePosts = async ({actionType, order, posts, previousPostId = '', prepareRecordsOnly = false}: HandlePostsArgs): Promise<Model[]> => {
const tableName = POST;

View file

@ -112,7 +112,7 @@ export const transformFileRecord = ({action, database, value}: TransformerArgs):
file.width = raw?.width || record?.width || 0;
file.height = raw?.height || record?.height || 0;
file.imageThumbnail = raw?.mini_preview || record?.imageThumbnail || '';
file.localPath = raw?.localPath || record?.localPath || '';
file.localPath = raw?.localPath || record?.localPath || null;
};
return prepareBaseRecord({

View file

@ -43,7 +43,7 @@ export const transformUserRecord = ({action, database, value}: TransformerArgs):
user.props = raw.props || null;
user.timezone = raw.timezone || null;
user.isBot = raw.is_bot;
user.remoteId = raw?.remote_id ?? '';
user.remoteId = raw?.remote_id ?? null;
if (raw.status) {
user.status = raw.status;
}

View file

@ -10,12 +10,12 @@ const {CATEGORY} = MM_TABLES.SERVER;
export default tableSchema({
name: CATEGORY,
columns: [
{name: 'collapsed', type: 'boolean'},
{name: 'display_name', type: 'string'},
{name: 'type', type: 'string'},
{name: 'muted', type: 'boolean'},
{name: 'sort_order', type: 'number'},
{name: 'sorting', type: 'string'},
{name: 'muted', type: 'boolean'},
{name: 'collapsed', type: 'boolean'},
{name: 'team_id', type: 'string', isIndexed: true},
{name: 'type', type: 'string'},
],
});

View file

@ -10,6 +10,6 @@ const {CUSTOM_EMOJI} = MM_TABLES.SERVER;
export default tableSchema({
name: CUSTOM_EMOJI,
columns: [
{name: 'name', type: 'string'},
{name: 'name', type: 'string', isIndexed: true},
],
});

View file

@ -13,7 +13,7 @@ export default tableSchema({
{name: 'extension', type: 'string'},
{name: 'height', type: 'number'},
{name: 'image_thumbnail', type: 'string'},
{name: 'local_path', type: 'string'},
{name: 'local_path', type: 'string', isOptional: true},
{name: 'mime_type', type: 'string'},
{name: 'name', type: 'string'},
{name: 'post_id', type: 'string', isIndexed: true},

View file

@ -10,13 +10,14 @@ const {MY_CHANNEL} = MM_TABLES.SERVER;
export default tableSchema({
name: MY_CHANNEL,
columns: [
{name: 'is_unread', type: 'boolean'},
{name: 'last_post_at', type: 'number'},
{name: 'last_viewed_at', type: 'number'},
{name: 'manually_unread', type: 'boolean'},
{name: 'mentions_count', type: 'number'},
{name: 'message_count', type: 'number'},
{name: 'is_unread', type: 'boolean'},
{name: 'roles', type: 'string'},
{name: 'viewed_at', type: 'number'},
],
});

View file

@ -13,7 +13,6 @@ export default tableSchema({
{name: 'channel_id', type: 'string', isIndexed: true},
{name: 'create_at', type: 'number'},
{name: 'delete_at', type: 'number'},
{name: 'update_at', type: 'number'},
{name: 'edit_at', type: 'number'},
{name: 'is_pinned', type: 'boolean'},
{name: 'message', type: 'string'},
@ -24,6 +23,8 @@ export default tableSchema({
{name: 'props', type: 'string'},
{name: 'root_id', type: 'string'},
{name: 'type', type: 'string'},
{name: 'update_at', type: 'number'},
{name: 'user_id', type: 'string', isIndexed: true},
],
});

View file

@ -10,8 +10,8 @@ const {POSTS_IN_THREAD} = MM_TABLES.SERVER;
export default tableSchema({
name: POSTS_IN_THREAD,
columns: [
{name: 'root_id', type: 'string', isIndexed: true},
{name: 'earliest', type: 'number'},
{name: 'latest', type: 'number'},
{name: 'root_id', type: 'string', isIndexed: true},
],
});

View file

@ -16,3 +16,4 @@ export default tableSchema({
{name: 'user_id', type: 'string', isIndexed: true},
],
});

View file

@ -10,7 +10,7 @@ const {ROLE} = MM_TABLES.SERVER;
export default tableSchema({
name: ROLE,
columns: [
{name: 'name', type: 'string'},
{name: 'name', type: 'string', isIndexed: true},
{name: 'permissions', type: 'string'},
],
});

View file

@ -10,10 +10,10 @@ const {TEAM} = MM_TABLES.SERVER;
export default tableSchema({
name: TEAM,
columns: [
{name: 'is_allow_open_invite', type: 'boolean'},
{name: 'allowed_domains', type: 'string'},
{name: 'description', type: 'string'},
{name: 'display_name', type: 'string'},
{name: 'is_allow_open_invite', type: 'boolean'},
{name: 'is_group_constrained', type: 'boolean'},
{name: 'last_team_icon_updated_at', type: 'number'},
{name: 'name', type: 'string'},

View file

@ -16,3 +16,4 @@ export default tableSchema({
{name: 'term', type: 'string'},
],
});

View file

@ -10,12 +10,13 @@ const {THREAD} = MM_TABLES.SERVER;
export default tableSchema({
name: THREAD,
columns: [
{name: 'is_following', type: 'boolean'},
{name: 'last_reply_at', type: 'number'},
{name: 'last_viewed_at', type: 'number'},
{name: 'is_following', type: 'boolean'},
{name: 'reply_count', type: 'number'},
{name: 'unread_replies', type: 'number'},
{name: 'unread_mentions', type: 'number'},
{name: 'unread_replies', type: 'number'},
{name: 'viewed_at', type: 'number'},
],
});

View file

@ -10,8 +10,8 @@ const {THREADS_IN_TEAM} = MM_TABLES.SERVER;
export default tableSchema({
name: THREADS_IN_TEAM,
columns: [
{name: 'loaded_in_global_threads', type: 'boolean', isIndexed: true},
{name: 'team_id', type: 'string', isIndexed: true},
{name: 'thread_id', type: 'string', isIndexed: true},
{name: 'loaded_in_global_threads', type: 'boolean', isIndexed: true},
],
});

View file

@ -11,7 +11,6 @@ export default tableSchema({
name: USER,
columns: [
{name: 'auth_service', type: 'string'},
{name: 'update_at', type: 'number'},
{name: 'delete_at', type: 'number'},
{name: 'email', type: 'string'},
{name: 'first_name', type: 'string'},
@ -24,10 +23,11 @@ export default tableSchema({
{name: 'notify_props', type: 'string'},
{name: 'position', type: 'string'},
{name: 'props', type: 'string'},
{name: 'remote_id', type: 'string', isOptional: true},
{name: 'roles', type: 'string'},
{name: 'status', type: 'string'},
{name: 'timezone', type: 'string'},
{name: 'update_at', type: 'number'},
{name: 'username', type: 'string'},
{name: 'remote_id', type: 'string', isOptional: true},
],
});

View file

@ -45,22 +45,22 @@ describe('*** Test schema for SERVER database ***', () => {
name: CATEGORY,
unsafeSql: undefined,
columns: {
collapsed: {name: 'collapsed', type: 'boolean'},
display_name: {name: 'display_name', type: 'string'},
type: {name: 'type', type: 'string'},
muted: {name: 'muted', type: 'boolean'},
sort_order: {name: 'sort_order', type: 'number'},
sorting: {name: 'sorting', type: 'string'},
muted: {name: 'muted', type: 'boolean'},
collapsed: {name: 'collapsed', type: 'boolean'},
team_id: {name: 'team_id', type: 'string', isIndexed: true},
type: {name: 'type', type: 'string'},
},
columnArray: [
{name: 'collapsed', type: 'boolean'},
{name: 'display_name', type: 'string'},
{name: 'type', type: 'string'},
{name: 'muted', type: 'boolean'},
{name: 'sort_order', type: 'number'},
{name: 'sorting', type: 'string'},
{name: 'muted', type: 'boolean'},
{name: 'collapsed', type: 'boolean'},
{name: 'team_id', type: 'string', isIndexed: true},
{name: 'type', type: 'string'},
],
},
[CATEGORY_CHANNEL]: {
@ -143,30 +143,30 @@ describe('*** Test schema for SERVER database ***', () => {
name: CUSTOM_EMOJI,
unsafeSql: undefined,
columns: {
name: {name: 'name', type: 'string'},
name: {name: 'name', type: 'string', isIndexed: true},
},
columnArray: [{name: 'name', type: 'string'}],
columnArray: [{name: 'name', type: 'string', isIndexed: true}],
},
[MY_CHANNEL]: {
name: MY_CHANNEL,
unsafeSql: undefined,
columns: {
is_unread: {name: 'is_unread', type: 'boolean'},
last_post_at: {name: 'last_post_at', type: 'number'},
last_viewed_at: {name: 'last_viewed_at', type: 'number'},
manually_unread: {name: 'manually_unread', type: 'boolean'},
mentions_count: {name: 'mentions_count', type: 'number'},
message_count: {name: 'message_count', type: 'number'},
is_unread: {name: 'is_unread', type: 'boolean'},
roles: {name: 'roles', type: 'string'},
viewed_at: {name: 'viewed_at', type: 'number'},
},
columnArray: [
{name: 'is_unread', type: 'boolean'},
{name: 'last_post_at', type: 'number'},
{name: 'last_viewed_at', type: 'number'},
{name: 'manually_unread', type: 'boolean'},
{name: 'mentions_count', type: 'number'},
{name: 'message_count', type: 'number'},
{name: 'is_unread', type: 'boolean'},
{name: 'roles', type: 'string'},
{name: 'viewed_at', type: 'number'},
],
@ -218,7 +218,7 @@ describe('*** Test schema for SERVER database ***', () => {
extension: {name: 'extension', type: 'string'},
height: {name: 'height', type: 'number'},
image_thumbnail: {name: 'image_thumbnail', type: 'string'},
local_path: {name: 'local_path', type: 'string'},
local_path: {name: 'local_path', type: 'string', isOptional: true},
mime_type: {name: 'mime_type', type: 'string'},
name: {name: 'name', type: 'string'},
post_id: {name: 'post_id', type: 'string', isIndexed: true},
@ -229,7 +229,7 @@ describe('*** Test schema for SERVER database ***', () => {
{name: 'extension', type: 'string'},
{name: 'height', type: 'number'},
{name: 'image_thumbnail', type: 'string'},
{name: 'local_path', type: 'string'},
{name: 'local_path', type: 'string', isOptional: true},
{name: 'mime_type', type: 'string'},
{name: 'name', type: 'string'},
{name: 'post_id', type: 'string', isIndexed: true},
@ -241,14 +241,14 @@ describe('*** Test schema for SERVER database ***', () => {
name: POSTS_IN_THREAD,
unsafeSql: undefined,
columns: {
root_id: {name: 'root_id', type: 'string', isIndexed: true},
earliest: {name: 'earliest', type: 'number'},
latest: {name: 'latest', type: 'number'},
root_id: {name: 'root_id', type: 'string', isIndexed: true},
},
columnArray: [
{name: 'root_id', type: 'string', isIndexed: true},
{name: 'earliest', type: 'number'},
{name: 'latest', type: 'number'},
{name: 'root_id', type: 'string', isIndexed: true},
],
},
[POST]: {
@ -258,7 +258,6 @@ describe('*** Test schema for SERVER database ***', () => {
channel_id: {name: 'channel_id', type: 'string', isIndexed: true},
create_at: {name: 'create_at', type: 'number'},
delete_at: {name: 'delete_at', type: 'number'},
update_at: {name: 'update_at', type: 'number'},
edit_at: {name: 'edit_at', type: 'number'},
is_pinned: {name: 'is_pinned', type: 'boolean'},
message: {name: 'message', type: 'string'},
@ -269,13 +268,13 @@ describe('*** Test schema for SERVER database ***', () => {
props: {name: 'props', type: 'string'},
root_id: {name: 'root_id', type: 'string'},
type: {name: 'type', type: 'string'},
update_at: {name: 'update_at', type: 'number'},
user_id: {name: 'user_id', type: 'string', isIndexed: true},
},
columnArray: [
{name: 'channel_id', type: 'string', isIndexed: true},
{name: 'create_at', type: 'number'},
{name: 'delete_at', type: 'number'},
{name: 'update_at', type: 'number'},
{name: 'edit_at', type: 'number'},
{name: 'is_pinned', type: 'boolean'},
{name: 'message', type: 'string'},
@ -286,6 +285,7 @@ describe('*** Test schema for SERVER database ***', () => {
{name: 'props', type: 'string'},
{name: 'root_id', type: 'string'},
{name: 'type', type: 'string'},
{name: 'update_at', type: 'number'},
{name: 'user_id', type: 'string', isIndexed: true},
],
},
@ -335,11 +335,11 @@ describe('*** Test schema for SERVER database ***', () => {
name: ROLE,
unsafeSql: undefined,
columns: {
name: {name: 'name', type: 'string'},
name: {name: 'name', type: 'string', isIndexed: true},
permissions: {name: 'permissions', type: 'string'},
},
columnArray: [
{name: 'name', type: 'string'},
{name: 'name', type: 'string', isIndexed: true},
{name: 'permissions', type: 'string'},
],
},
@ -357,13 +357,13 @@ describe('*** Test schema for SERVER database ***', () => {
name: TEAM,
unsafeSql: undefined,
columns: {
allowed_domains: {name: 'allowed_domains', type: 'string'},
description: {name: 'description', type: 'string'},
display_name: {name: 'display_name', type: 'string'},
is_allow_open_invite: {
name: 'is_allow_open_invite',
type: 'boolean',
},
allowed_domains: {name: 'allowed_domains', type: 'string'},
description: {name: 'description', type: 'string'},
display_name: {name: 'display_name', type: 'string'},
is_group_constrained: {
name: 'is_group_constrained',
type: 'boolean',
@ -377,10 +377,10 @@ describe('*** Test schema for SERVER database ***', () => {
update_at: {name: 'update_at', type: 'number'},
},
columnArray: [
{name: 'is_allow_open_invite', type: 'boolean'},
{name: 'allowed_domains', type: 'string'},
{name: 'description', type: 'string'},
{name: 'display_name', type: 'string'},
{name: 'is_allow_open_invite', type: 'boolean'},
{name: 'is_group_constrained', type: 'boolean'},
{name: 'last_team_icon_updated_at', type: 'number'},
{name: 'name', type: 'string'},
@ -430,21 +430,21 @@ describe('*** Test schema for SERVER database ***', () => {
name: THREAD,
unsafeSql: undefined,
columns: {
is_following: {name: 'is_following', type: 'boolean'},
last_reply_at: {name: 'last_reply_at', type: 'number'},
last_viewed_at: {name: 'last_viewed_at', type: 'number'},
is_following: {name: 'is_following', type: 'boolean'},
reply_count: {name: 'reply_count', type: 'number'},
unread_replies: {name: 'unread_replies', type: 'number'},
unread_mentions: {name: 'unread_mentions', type: 'number'},
unread_replies: {name: 'unread_replies', type: 'number'},
viewed_at: {name: 'viewed_at', type: 'number'},
},
columnArray: [
{name: 'is_following', type: 'boolean'},
{name: 'last_reply_at', type: 'number'},
{name: 'last_viewed_at', type: 'number'},
{name: 'is_following', type: 'boolean'},
{name: 'reply_count', type: 'number'},
{name: 'unread_replies', type: 'number'},
{name: 'unread_mentions', type: 'number'},
{name: 'unread_replies', type: 'number'},
{name: 'viewed_at', type: 'number'},
],
},
@ -464,14 +464,14 @@ describe('*** Test schema for SERVER database ***', () => {
name: THREADS_IN_TEAM,
unsafeSql: undefined,
columns: {
loaded_in_global_threads: {name: 'loaded_in_global_threads', type: 'boolean', isIndexed: true},
team_id: {name: 'team_id', type: 'string', isIndexed: true},
thread_id: {name: 'thread_id', type: 'string', isIndexed: true},
loaded_in_global_threads: {name: 'loaded_in_global_threads', type: 'boolean', isIndexed: true},
},
columnArray: [
{name: 'loaded_in_global_threads', type: 'boolean', isIndexed: true},
{name: 'team_id', type: 'string', isIndexed: true},
{name: 'thread_id', type: 'string', isIndexed: true},
{name: 'loaded_in_global_threads', type: 'boolean', isIndexed: true},
],
},
[USER]: {
@ -479,31 +479,27 @@ describe('*** Test schema for SERVER database ***', () => {
unsafeSql: undefined,
columns: {
auth_service: {name: 'auth_service', type: 'string'},
update_at: {name: 'update_at', type: 'number'},
delete_at: {name: 'delete_at', type: 'number'},
email: {name: 'email', type: 'string'},
first_name: {name: 'first_name', type: 'string'},
is_bot: {name: 'is_bot', type: 'boolean'},
is_guest: {name: 'is_guest', type: 'boolean'},
last_name: {name: 'last_name', type: 'string'},
last_picture_update: {
name: 'last_picture_update',
type: 'number',
},
last_picture_update: {name: 'last_picture_update', type: 'number'},
locale: {name: 'locale', type: 'string'},
nickname: {name: 'nickname', type: 'string'},
notify_props: {name: 'notify_props', type: 'string'},
position: {name: 'position', type: 'string'},
props: {name: 'props', type: 'string'},
remote_id: {name: 'remote_id', type: 'string', isOptional: true},
roles: {name: 'roles', type: 'string'},
status: {name: 'status', type: 'string'},
timezone: {name: 'timezone', type: 'string'},
update_at: {name: 'update_at', type: 'number'},
username: {name: 'username', type: 'string'},
remote_id: {name: 'remote_id', type: 'string', isOptional: true},
},
columnArray: [
{name: 'auth_service', type: 'string'},
{name: 'update_at', type: 'number'},
{name: 'delete_at', type: 'number'},
{name: 'email', type: 'string'},
{name: 'first_name', type: 'string'},
@ -516,11 +512,12 @@ describe('*** Test schema for SERVER database ***', () => {
{name: 'notify_props', type: 'string'},
{name: 'position', type: 'string'},
{name: 'props', type: 'string'},
{name: 'remote_id', type: 'string', isOptional: true},
{name: 'roles', type: 'string'},
{name: 'status', type: 'string'},
{name: 'timezone', type: 'string'},
{name: 'update_at', type: 'number'},
{name: 'username', type: 'string'},
{name: 'remote_id', type: 'string', isOptional: true},
],
},
},

View file

@ -126,7 +126,7 @@ export const prepareMyChannelsForTeam = async (operator: ServerDataOperator, tea
export const prepareDeleteChannel = async (channel: ChannelModel): Promise<Model[]> => {
const preparedModels: Model[] = [channel.prepareDestroyPermanently()];
const relations: Array<Relation<Model> | undefined> = [channel.membership, channel.info, channel.settings, channel.categoryChannel];
const relations: Array<Relation<Model> | undefined> = [channel.membership, channel.info, channel.categoryChannel];
await Promise.all(relations.map(async (relation) => {
try {
const model = await relation?.fetch();

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 KiB

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,586 @@
-- Exported from QuickDBD: https://www.quickdatabasediagrams.com/
-- Link to schema: https://app.quickdatabasediagrams.com/#/d/EZ0IyA
-- NOTE! If you have used non-SQL datatypes in your design, you will have to change these here.
-- Server Database - Schema Version 1
-- 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.
SET XACT_ABORT ON
BEGIN TRANSACTION QUICKDBD
CREATE TABLE [Category] (
-- server-generated
[id] string NOT NULL ,
[collapsed] bool NOT NULL ,
[display_name] string NOT NULL ,
[muted] bool NOT NULL ,
[sort_order] number NOT NULL ,
-- alpha, recent, manual
[sorting] string NOT NULL ,
[team_id] string NOT NULL ,
-- 'channels' | 'direct_messages' | 'favorites' | 'custom'
[type] string NOT NULL ,
CONSTRAINT [PK_Category] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [CategoryChannel] (
-- composition ID Team.id_Channel.id
[id] string NOT NULL ,
[category_id] string NOT NULL ,
[channel_id] string NOT NULL ,
[sort_order] number NOT NULL ,
CONSTRAINT [PK_CategoryChannel] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [Channel] (
-- server-generated
[id] string NOT NULL ,
[create_at] string NOT NULL ,
[creator_id] string NOT NULL ,
[delete_at] number NOT NULL ,
[display_name] string NOT NULL ,
[is_group_constrained] bool NOT NULL ,
[name] string NOT NULL ,
[shared] bool NOT NULL ,
[team_id] string NOT NULL ,
[type] string NOT NULL ,
[update_at] number NOT NULL ,
CONSTRAINT [PK_Channel] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [ChannelInfo] (
-- same value as Channel.id
[id] string NOT NULL ,
[guest_count] number NOT NULL ,
[header] string NOT NULL ,
[member_count] number NOT NULL ,
[pinned_post_count] number NOT NULL ,
[purpose] string NOT NULL ,
CONSTRAINT [PK_ChannelInfo] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [ChannelMembership] (
-- composition ID Channel.id-User.id
[id] string NOT NULL ,
[channel_id] string NOT NULL ,
[user_id] string NOT NULL ,
CONSTRAINT [PK_ChannelMembership] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [CustomEmoji] (
-- auto-generated
[id] string NOT NULL ,
[name] string NOT NULL ,
CONSTRAINT [PK_CustomEmoji] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [Draft] (
-- auto-generated
[id] string NOT NULL ,
[channel_id] string NOT NULL ,
-- stringify (array)
[files] string NOT NULL ,
[message] string NOT NULL ,
[root_id] string NULL ,
CONSTRAINT [PK_Draft] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [File] (
-- server-generated
[id] string NOT NULL ,
[extension] string NOT NULL ,
[height] number NOT NULL ,
-- base64 data string or filepath for video thumbnails
[image_thumbnail] string NOT NULL ,
[local_path] string NULL ,
[mime_type] string NOT NULL ,
[name] string NOT NULL ,
[post_id] string NOT NULL ,
[size] number NOT NULL ,
[width] number NOT NULL ,
CONSTRAINT [PK_File] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [MyChannel] (
-- same as Channel.id
[id] string NOT NULL ,
[is_unread] boolean NOT NULL ,
[last_post_at] number NOT NULL ,
[last_viewed_at] number NOT NULL ,
[manually_unread] boolean NOT NULL ,
[mentions_count] number NOT NULL ,
[message_count] number NOT NULL ,
[roles] string NOT NULL ,
[viewed_at] number NOT NULL ,
CONSTRAINT [PK_MyChannel] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [MyChannelSettings] (
-- same as Channel.id
[id] string NOT NULL ,
[notify_props] string NOT NULL ,
CONSTRAINT [PK_MyChannelSettings] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [MyTeam] (
-- same as Team.id
[id] string NOT NULL ,
[roles] string NOT NULL ,
CONSTRAINT [PK_MyTeam] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [Post] (
-- server generated
[id] string NOT NULL ,
[channel_id] string NOT NULL ,
[create_at] number NOT NULL ,
[delete_at] number NOT NULL ,
[edit_at] number NOT NULL ,
[is_pinned] boolean NOT NULL ,
[message] string NOT NULL ,
[metadata] string NULL ,
[original_id] string NOT NULL ,
[pending_post_id] string NOT NULL ,
[previous_post_id] string NOT NULL ,
[props] string NOT NULL ,
[root_id] string NOT NULL ,
[type] string NOT NULL ,
[update_at] number NOT NULL ,
[user_id] string NOT NULL ,
CONSTRAINT [PK_Post] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [PostsInChannel] (
-- auto-generated
[id] string NOT NULL ,
[channel_id] string NOT NULL ,
[earliest] number NOT NULL ,
[latest] number NOT NULL ,
CONSTRAINT [PK_PostsInChannel] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [PostsInThread] (
-- auto-generated
[id] string NOT NULL ,
[earliest] number NOT NULL ,
[latest] number NOT NULL ,
[root_id] string NOT NULL ,
CONSTRAINT [PK_PostsInThread] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [Preference] (
-- server-generated
[id] string NOT NULL ,
[category] string NOT NULL ,
[name] string NOT NULL ,
[user_id] string NOT NULL ,
[value] string NOT NULL ,
CONSTRAINT [PK_Preference] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [Reaction] (
-- server-generated
[id] string NOT NULL ,
[create_at] number NOT NULL ,
[emoji_name] string NOT NULL ,
[post_id] string NOT NULL ,
[user_id] string NOT NULL ,
CONSTRAINT [PK_Reaction] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [Role] (
-- server-generated
[id] string NOT NULL ,
[name] string NOT NULL ,
-- stringify array
[permissions] string NOT NULL ,
CONSTRAINT [PK_Role] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [System] (
-- SYSTEM_IDENTIFIERS
[id] string NOT NULL ,
[value] string NOT NULL ,
CONSTRAINT [PK_System] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [Team] (
-- server-generated
[id] string NOT NULL ,
[allowed_domains] string NOT NULL ,
[description] string NOT NULL ,
[display_name] string NOT NULL ,
[is_allow_open_invite] boolean NOT NULL ,
[is_group_constrained] boolean NOT NULL ,
[last_team_icon_updated_at] number NOT NULL ,
[name] string NOT NULL ,
[type] string NOT NULL ,
[update_at] number NOT NULL ,
CONSTRAINT [PK_Team] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [TeamChannelHistory] (
-- same as Team.id
[id] string NOT NULL ,
-- stringified JSON array; FIFO
[channel_ids] string NOT NULL ,
CONSTRAINT [PK_TeamChannelHistory] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [TeamMembership] (
-- auto-generated
[id] string NOT NULL ,
[team_id] string NOT NULL ,
[user_id] string NOT NULL ,
CONSTRAINT [PK_TeamMembership] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [TeamSearchHistory] (
-- auto-generated
[id] string NOT NULL ,
[created_at] number NOT NULL ,
[display_term] string NOT NULL ,
[team_id] string NOT NULL ,
[term] string NOT NULL ,
CONSTRAINT [PK_TeamSearchHistory] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [Thread] (
-- similar to Post.id but for root post only
[id] string NOT NULL ,
[is_following] boolean NOT NULL ,
[last_reply_at] number NOT NULL ,
[last_viewed_at] number NOT NULL ,
[reply_count] number NOT NULL ,
[unread_mentions] number NOT NULL ,
[unread_replies] number NOT NULL ,
[viewed_at] number NOT NULL ,
CONSTRAINT [PK_Thread] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [ThreadsInTeam] (
-- auto-generated
[id] string NOT NULL ,
[loaded_in_global_threads] boolean NOT NULL ,
[team_id] string NOT NULL ,
[thread_id] string NOT NULL ,
CONSTRAINT [PK_ThreadsInTeam] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
-- who is participating in this conversation
CREATE TABLE [ThreadParticipant] (
-- auto-generated
[id] string NOT NULL ,
[thread_id] string NOT NULL ,
[user_id] string NOT NULL ,
CONSTRAINT [PK_ThreadParticipant] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
CREATE TABLE [User] (
-- server generated
[id] string NOT NULL ,
[auth_service] string NOT NULL ,
[delete_at] number NOT NULL ,
[email] string NOT NULL ,
[first_name] string NOT NULL ,
[is_bot] boolean NOT NULL ,
[is_guest] boolean NOT NULL ,
[last_name] string NOT NULL ,
[last_picture_update] number NOT NULL ,
[locale] string NOT NULL ,
[nickname] string NOT NULL ,
[notify_props] string NOT NULL ,
[position] string NOT NULL ,
[props] string NOT NULL ,
[remote_id] string NULL ,
[roles] string NOT NULL ,
[status] string NOT NULL ,
[timezone] string NOT NULL ,
[update_at] number NOT NULL ,
[username] string NOT NULL ,
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED (
[id] ASC
)
)
ALTER TABLE [Category] WITH CHECK ADD CONSTRAINT [FK_Category_team_id] FOREIGN KEY([team_id])
REFERENCES [Team] ([id])
ALTER TABLE [Category] CHECK CONSTRAINT [FK_Category_team_id]
ALTER TABLE [CategoryChannel] WITH CHECK ADD CONSTRAINT [FK_CategoryChannel_category_id] FOREIGN KEY([category_id])
REFERENCES [Category] ([id])
ALTER TABLE [CategoryChannel] CHECK CONSTRAINT [FK_CategoryChannel_category_id]
ALTER TABLE [Channel] WITH CHECK ADD CONSTRAINT [FK_Channel_id] FOREIGN KEY([id])
REFERENCES [CategoryChannel] ([channel_id])
ALTER TABLE [Channel] CHECK CONSTRAINT [FK_Channel_id]
ALTER TABLE [Channel] WITH CHECK ADD CONSTRAINT [FK_Channel_creator_id] FOREIGN KEY([creator_id])
REFERENCES [User] ([id])
ALTER TABLE [Channel] CHECK CONSTRAINT [FK_Channel_creator_id]
ALTER TABLE [Channel] WITH CHECK ADD CONSTRAINT [FK_Channel_team_id] FOREIGN KEY([team_id])
REFERENCES [Team] ([id])
ALTER TABLE [Channel] CHECK CONSTRAINT [FK_Channel_team_id]
ALTER TABLE [ChannelInfo] WITH CHECK ADD CONSTRAINT [FK_ChannelInfo_id] FOREIGN KEY([id])
REFERENCES [Channel] ([id])
ALTER TABLE [ChannelInfo] CHECK CONSTRAINT [FK_ChannelInfo_id]
ALTER TABLE [ChannelMembership] WITH CHECK ADD CONSTRAINT [FK_ChannelMembership_channel_id] FOREIGN KEY([channel_id])
REFERENCES [Channel] ([id])
ALTER TABLE [ChannelMembership] CHECK CONSTRAINT [FK_ChannelMembership_channel_id]
ALTER TABLE [ChannelMembership] WITH CHECK ADD CONSTRAINT [FK_ChannelMembership_user_id] FOREIGN KEY([user_id])
REFERENCES [User] ([id])
ALTER TABLE [ChannelMembership] CHECK CONSTRAINT [FK_ChannelMembership_user_id]
ALTER TABLE [Draft] WITH CHECK ADD CONSTRAINT [FK_Draft_channel_id] FOREIGN KEY([channel_id])
REFERENCES [Channel] ([id])
ALTER TABLE [Draft] CHECK CONSTRAINT [FK_Draft_channel_id]
ALTER TABLE [Draft] WITH CHECK ADD CONSTRAINT [FK_Draft_root_id] FOREIGN KEY([root_id])
REFERENCES [Post] ([id])
ALTER TABLE [Draft] CHECK CONSTRAINT [FK_Draft_root_id]
ALTER TABLE [File] WITH CHECK ADD CONSTRAINT [FK_File_post_id] FOREIGN KEY([post_id])
REFERENCES [Post] ([id])
ALTER TABLE [File] CHECK CONSTRAINT [FK_File_post_id]
ALTER TABLE [MyChannel] WITH CHECK ADD CONSTRAINT [FK_MyChannel_id] FOREIGN KEY([id])
REFERENCES [Channel] ([id])
ALTER TABLE [MyChannel] CHECK CONSTRAINT [FK_MyChannel_id]
ALTER TABLE [MyChannelSettings] WITH CHECK ADD CONSTRAINT [FK_MyChannelSettings_id] FOREIGN KEY([id])
REFERENCES [MyChannel] ([id])
ALTER TABLE [MyChannelSettings] CHECK CONSTRAINT [FK_MyChannelSettings_id]
ALTER TABLE [MyTeam] WITH CHECK ADD CONSTRAINT [FK_MyTeam_id] FOREIGN KEY([id])
REFERENCES [Team] ([id])
ALTER TABLE [MyTeam] CHECK CONSTRAINT [FK_MyTeam_id]
ALTER TABLE [Post] WITH CHECK ADD CONSTRAINT [FK_Post_channel_id] FOREIGN KEY([channel_id])
REFERENCES [Channel] ([id])
ALTER TABLE [Post] CHECK CONSTRAINT [FK_Post_channel_id]
ALTER TABLE [Post] WITH CHECK ADD CONSTRAINT [FK_Post_user_id] FOREIGN KEY([user_id])
REFERENCES [User] ([id])
ALTER TABLE [Post] CHECK CONSTRAINT [FK_Post_user_id]
ALTER TABLE [PostsInChannel] WITH CHECK ADD CONSTRAINT [FK_PostsInChannel_channel_id] FOREIGN KEY([channel_id])
REFERENCES [Channel] ([id])
ALTER TABLE [PostsInChannel] CHECK CONSTRAINT [FK_PostsInChannel_channel_id]
ALTER TABLE [PostsInThread] WITH CHECK ADD CONSTRAINT [FK_PostsInThread_root_id] FOREIGN KEY([root_id])
REFERENCES [Post] ([id])
ALTER TABLE [PostsInThread] CHECK CONSTRAINT [FK_PostsInThread_root_id]
ALTER TABLE [Preference] WITH CHECK ADD CONSTRAINT [FK_Preference_user_id] FOREIGN KEY([user_id])
REFERENCES [User] ([id])
ALTER TABLE [Preference] CHECK CONSTRAINT [FK_Preference_user_id]
ALTER TABLE [Reaction] WITH CHECK ADD CONSTRAINT [FK_Reaction_post_id] FOREIGN KEY([post_id])
REFERENCES [Post] ([id])
ALTER TABLE [Reaction] CHECK CONSTRAINT [FK_Reaction_post_id]
ALTER TABLE [Reaction] WITH CHECK ADD CONSTRAINT [FK_Reaction_user_id] FOREIGN KEY([user_id])
REFERENCES [User] ([id])
ALTER TABLE [Reaction] CHECK CONSTRAINT [FK_Reaction_user_id]
ALTER TABLE [TeamChannelHistory] WITH CHECK ADD CONSTRAINT [FK_TeamChannelHistory_id] FOREIGN KEY([id])
REFERENCES [Team] ([id])
ALTER TABLE [TeamChannelHistory] CHECK CONSTRAINT [FK_TeamChannelHistory_id]
ALTER TABLE [TeamMembership] WITH CHECK ADD CONSTRAINT [FK_TeamMembership_team_id] FOREIGN KEY([team_id])
REFERENCES [Team] ([id])
ALTER TABLE [TeamMembership] CHECK CONSTRAINT [FK_TeamMembership_team_id]
ALTER TABLE [TeamMembership] WITH CHECK ADD CONSTRAINT [FK_TeamMembership_user_id] FOREIGN KEY([user_id])
REFERENCES [User] ([id])
ALTER TABLE [TeamMembership] CHECK CONSTRAINT [FK_TeamMembership_user_id]
ALTER TABLE [TeamSearchHistory] WITH CHECK ADD CONSTRAINT [FK_TeamSearchHistory_team_id] FOREIGN KEY([team_id])
REFERENCES [Team] ([id])
ALTER TABLE [TeamSearchHistory] CHECK CONSTRAINT [FK_TeamSearchHistory_team_id]
ALTER TABLE [Thread] WITH CHECK ADD CONSTRAINT [FK_Thread_id] FOREIGN KEY([id])
REFERENCES [Post] ([id])
ALTER TABLE [Thread] CHECK CONSTRAINT [FK_Thread_id]
ALTER TABLE [ThreadsInTeam] WITH CHECK ADD CONSTRAINT [FK_ThreadsInTeam_team_id] FOREIGN KEY([team_id])
REFERENCES [Team] ([id])
ALTER TABLE [ThreadsInTeam] CHECK CONSTRAINT [FK_ThreadsInTeam_team_id]
ALTER TABLE [ThreadsInTeam] WITH CHECK ADD CONSTRAINT [FK_ThreadsInTeam_thread_id] FOREIGN KEY([thread_id])
REFERENCES [Thread] ([id])
ALTER TABLE [ThreadsInTeam] CHECK CONSTRAINT [FK_ThreadsInTeam_thread_id]
ALTER TABLE [ThreadParticipant] WITH CHECK ADD CONSTRAINT [FK_ThreadParticipant_thread_id] FOREIGN KEY([thread_id])
REFERENCES [Thread] ([id])
ALTER TABLE [ThreadParticipant] CHECK CONSTRAINT [FK_ThreadParticipant_thread_id]
ALTER TABLE [ThreadParticipant] WITH CHECK ADD CONSTRAINT [FK_ThreadParticipant_user_id] FOREIGN KEY([user_id])
REFERENCES [User] ([id])
ALTER TABLE [ThreadParticipant] CHECK CONSTRAINT [FK_ThreadParticipant_user_id]
CREATE INDEX [idx_Category_team_id]
ON [Category] ([team_id])
CREATE INDEX [idx_CategoryChannel_category_id]
ON [CategoryChannel] ([category_id])
CREATE INDEX [idx_CategoryChannel_channel_id]
ON [CategoryChannel] ([channel_id])
CREATE INDEX [idx_Channel_creator_id]
ON [Channel] ([creator_id])
CREATE INDEX [idx_Channel_name]
ON [Channel] ([name])
CREATE INDEX [idx_Channel_team_id]
ON [Channel] ([team_id])
CREATE INDEX [idx_ChannelMembership_channel_id]
ON [ChannelMembership] ([channel_id])
CREATE INDEX [idx_ChannelMembership_user_id]
ON [ChannelMembership] ([user_id])
CREATE INDEX [idx_Draft_channel_id]
ON [Draft] ([channel_id])
CREATE INDEX [idx_Draft_root_id]
ON [Draft] ([root_id])
CREATE INDEX [idx_File_post_id]
ON [File] ([post_id])
CREATE INDEX [idx_Post_channel_id]
ON [Post] ([channel_id])
CREATE INDEX [idx_Post_pending_post_id]
ON [Post] ([pending_post_id])
CREATE INDEX [idx_Post_user_id]
ON [Post] ([user_id])
CREATE INDEX [idx_PostsInChannel_channel_id]
ON [PostsInChannel] ([channel_id])
CREATE INDEX [idx_Preference_category]
ON [Preference] ([category])
CREATE INDEX [idx_Preference_user_id]
ON [Preference] ([user_id])
CREATE INDEX [idx_Reaction_post_id]
ON [Reaction] ([post_id])
CREATE INDEX [idx_Reaction_user_id]
ON [Reaction] ([user_id])
CREATE INDEX [idx_TeamMembership_team_id]
ON [TeamMembership] ([team_id])
CREATE INDEX [idx_TeamMembership_user_id]
ON [TeamMembership] ([user_id])
CREATE INDEX [idx_ThreadsInTeam_team_id]
ON [ThreadsInTeam] ([team_id])
CREATE INDEX [idx_ThreadsInTeam_thread_id]
ON [ThreadsInTeam] ([thread_id])
CREATE INDEX [idx_ThreadParticipant_thread_id]
ON [ThreadParticipant] ([thread_id])
CREATE INDEX [idx_ThreadParticipant_user_id]
ON [ThreadParticipant] ([user_id])
COMMIT TRANSACTION QUICKDBD

View file

@ -0,0 +1,265 @@
# Server Database - Schema Version 1
# 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
ChannelInfo
-
id PK string FK - Channel.id# same value as Channel.id
guest_count number
header string
member_count number
pinned_post_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
CustomEmoji
-
id PK string # auto-generated
name 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
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
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
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
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
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
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
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

View file

@ -9,7 +9,6 @@ import type ChannelInfoModel from './channel_info';
import type ChannelMembershipModel from './channel_membership';
import type DraftModel from './draft';
import type MyChannelModel from './my_channel';
import type MyChannelSettingsModel from './my_channel_settings';
import type PostModel from './post';
import type PostsInChannelModel from './posts_in_channel';
import type TeamModel from './team';
@ -79,9 +78,6 @@ export default class ChannelModel extends Model {
/** membership : Query returning the membership data for the current user if it belongs to this channel */
membership: Relation<MyChannelModel>;
/** settings: User specific settings/preferences for this channel */
settings: Relation<MyChannelSettingsModel>;
/** categoryChannel: category of this channel */
categoryChannel: Relation<CategoryChannelModel>;

View file

@ -26,7 +26,7 @@ export default class FileModel extends Model {
imageThumbnail: string;
/** local_path : Local path of the file that has been uploaded to server */
localPath: string;
localPath: string | null;
/** mime_type : The media type */
mimeType: string;

View file

@ -5,9 +5,10 @@ import {Relation} from '@nozbe/watermelondb';
import Model from '@nozbe/watermelondb/Model';
import type ChannelModel from './channel';
import type MyChannelSettingsModel from './my_channel_settings';
/**
* MyChannel is an extension of the Channel model but it lists only the Channels the app's user belongs to
* MyChannel is an extension of the Channel model, but it lists only the Channels the app's user belongs to
*/
export default class MyChannelModel extends Model {
/** table (name) : MyChannel */
@ -39,4 +40,7 @@ export default class MyChannelModel extends Model {
/** channel : The relation pointing to the CHANNEL table */
channel: Relation<ChannelModel>;
/** settings: User specific settings/preferences for this channel */
settings: Relation<MyChannelSettingsModel>;
}

View file

@ -4,7 +4,7 @@
import {Relation} from '@nozbe/watermelondb';
import Model from '@nozbe/watermelondb/Model';
import type ChannelModel from './channel';
import type MyChannelModel from './my_channel';
/**
* The MyChannelSettings model represents the specific user's configuration to
@ -17,6 +17,6 @@ export default class MyChannelSettingsModel extends Model {
/** notify_props : Configurations with regards to this channel */
notifyProps: Partial<ChannelNotifyProps>;
/** channel : The relation pointing to the CHANNEL table */
channel: Relation<ChannelModel>;
/** myChannel : The relation pointing to the MY_CHANNEL table */
myChannel: Relation<MyChannelModel>;
}