• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hicommonwealth / commonwealth / 14475911829

15 Apr 2025 05:42PM UTC coverage: 46.141% (-0.1%) from 46.287%
14475911829

Pull #11664

github

web-flow
Merge 7d47d0054 into 84ecac07e
Pull Request #11664: Added `XpChainEventCreated` support

1622 of 3876 branches covered (41.85%)

Branch coverage included in aggregate %.

0 of 25 new or added lines in 2 files covered. (0.0%)

58 existing lines in 14 files now uncovered.

2976 of 6089 relevant lines covered (48.88%)

39.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.67
/libs/model/src/models/thread.ts
1
import { CacheNamespaces, cache } from '@hicommonwealth/core';
2
import { Thread } from '@hicommonwealth/schemas';
3
import {
4
  MAX_TRUNCATED_CONTENT_LENGTH,
5
  getDecodedString,
6
} from '@hicommonwealth/shared';
7
import Sequelize from 'sequelize';
8
import { z } from 'zod';
9
import { emitEvent, getThreadContestManagers } from '../utils/utils';
10
import { AddressAttributes } from './address';
11
import type { CommunityAttributes } from './community';
12
import type { ThreadSubscriptionAttributes } from './thread_subscriptions';
13
import type { ModelInstance } from './types';
14
import { beforeValidateBodyHook } from './utils';
15

16
export type ThreadAttributes = z.infer<typeof Thread> & {
17
  // associations
18
  Community?: CommunityAttributes;
19
  subscriptions?: ThreadSubscriptionAttributes[];
20
};
21
export type ThreadInstance = ModelInstance<ThreadAttributes>;
22

23
export default (
24
  sequelize: Sequelize.Sequelize,
25
): Sequelize.ModelStatic<ThreadInstance> =>
26
  sequelize.define<ThreadInstance>(
104✔
27
    'Thread',
28
    {
29
      id: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true },
30
      address_id: { type: Sequelize.INTEGER, allowNull: true },
31
      created_by: { type: Sequelize.STRING, allowNull: true },
32
      title: { type: Sequelize.TEXT, allowNull: false },
33
      body: {
34
        type: Sequelize.STRING(MAX_TRUNCATED_CONTENT_LENGTH),
35
        allowNull: false,
36
      },
37
      kind: { type: Sequelize.STRING, allowNull: false },
38
      stage: {
39
        type: Sequelize.TEXT,
40
        allowNull: false,
41
        defaultValue: 'discussion',
42
      },
43
      url: { type: Sequelize.TEXT, allowNull: true },
44
      topic_id: { type: Sequelize.INTEGER, allowNull: false },
45
      pinned: {
46
        type: Sequelize.BOOLEAN,
47
        defaultValue: false,
48
        allowNull: false,
49
      },
50
      community_id: { type: Sequelize.STRING, allowNull: false },
51
      view_count: {
52
        type: Sequelize.INTEGER,
53
        allowNull: false,
54
        defaultValue: 0,
55
      },
56
      read_only: {
57
        type: Sequelize.BOOLEAN,
58
        allowNull: false,
59
        defaultValue: false,
60
      },
61
      links: { type: Sequelize.JSONB, allowNull: true },
62
      discord_meta: { type: Sequelize.JSONB, allowNull: true },
63
      has_poll: { type: Sequelize.BOOLEAN, allowNull: true },
64

65
      // canvas-related columns
66
      canvas_signed_data: { type: Sequelize.JSONB, allowNull: true },
67
      canvas_msg_id: { type: Sequelize.STRING, allowNull: true },
68
      // timestamps
69
      created_at: { type: Sequelize.DATE, allowNull: false },
70
      updated_at: { type: Sequelize.DATE, allowNull: false },
71
      last_edited: { type: Sequelize.DATE, allowNull: true },
72
      deleted_at: { type: Sequelize.DATE, allowNull: true },
73
      last_commented_on: { type: Sequelize.DATE, allowNull: true },
74
      marked_as_spam_at: { type: Sequelize.DATE, allowNull: true },
75
      archived_at: { type: Sequelize.DATE, allowNull: true },
76
      locked_at: {
77
        type: Sequelize.DATE,
78
        allowNull: true,
79
      },
80

81
      //counts
82
      reaction_count: {
83
        type: Sequelize.INTEGER,
84
        allowNull: false,
85
        defaultValue: 0,
86
      },
87
      reaction_weights_sum: {
88
        type: Sequelize.DECIMAL(78, 0),
89
        allowNull: false,
90
        defaultValue: 0,
91
      },
92
      comment_count: {
93
        type: Sequelize.INTEGER,
94
        allowNull: false,
95
        defaultValue: 0,
96
      },
97
      activity_rank_date: {
98
        type: Sequelize.DATE,
99
        allowNull: true,
100
        defaultValue: new Date(),
101
      },
102
      search: {
103
        type: Sequelize.TSVECTOR,
104
        allowNull: true,
105
      },
106
      content_url: { type: Sequelize.STRING, allowNull: true },
107
      is_linking_token: {
108
        type: Sequelize.BOOLEAN,
109
        defaultValue: false,
110
        allowNull: false,
111
      },
112
      launchpad_token_address: { type: Sequelize.STRING, allowNull: true },
113
    },
114
    {
115
      timestamps: true,
116
      createdAt: 'created_at',
117
      updatedAt: 'updated_at',
118
      deletedAt: 'deleted_at',
119
      underscored: true,
120
      tableName: 'Threads',
121
      paranoid: true,
122
      indexes: [
123
        { fields: ['address_id'] },
124
        { fields: ['community_id'] },
125
        { fields: ['community_id', 'created_at'] },
126
        { fields: ['community_id', 'updated_at'] },
127
        { fields: ['community_id', 'pinned'] },
128
        { fields: ['community_id', 'has_poll'] },
129
        { fields: ['canvas_msg_id'] },
130
      ],
131
      hooks: {
132
        beforeValidate(instance: ThreadInstance) {
133
          beforeValidateBodyHook(instance);
134✔
134
        },
135
        afterCreate: async (
136
          thread: ThreadInstance,
137
          options: Sequelize.CreateOptions<ThreadAttributes>,
138
        ) => {
139
          const { Outbox, Address } = sequelize.models;
41✔
140

141
          await cache().setKey(
41✔
142
            CacheNamespaces.Community_Thread_Count_Changed,
143
            thread.community_id,
144
            'true',
145
          );
146

147
          const { topic_id, community_id } = thread.get({
41✔
148
            plain: true,
149
          });
150
          const contestManagers = !topic_id
41✔
151
            ? []
152
            : await getThreadContestManagers(sequelize, topic_id, community_id);
153

154
          const address = (await Address.findByPk(
41✔
155
            thread.address_id,
156
          )) as AddressAttributes | null;
157

158
          await emitEvent(
41✔
159
            Outbox,
160
            [
161
              {
162
                event_name: 'ThreadCreated',
163
                event_payload: {
164
                  ...thread.get({ plain: true }),
165
                  address: address!.address,
166
                  contestManagers,
167
                },
168
              },
169
            ],
170
            options.transaction,
171
          );
172
        },
173
        afterDestroy: async (thread: ThreadInstance) => {
UNCOV
174
          await cache().setKey(
×
175
            CacheNamespaces.Community_Thread_Count_Changed,
176
            thread.community_id,
177
            'true',
178
          );
179
        },
180
      },
181
    },
182
  );
183

184
export function getThreadSearchVector(title: string, body: string) {
185
  return Sequelize.fn(
18✔
186
    'to_tsvector',
187
    'english',
188
    getDecodedString(title) + ' ' + getDecodedString(body),
189
  );
190
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc