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

hicommonwealth / commonwealth / 15889050983

25 Jun 2025 11:08PM UTC coverage: 40.088% (+0.03%) from 40.058%
15889050983

Pull #12505

github

web-flow
Merge aeedbfcaa into c4aad1abb
Pull Request #12505: Contests/Voting e2e test

1844 of 4993 branches covered (36.93%)

Branch coverage included in aggregate %.

3274 of 7774 relevant lines covered (42.11%)

37.1 hits per line

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

0.0
/libs/model/src/aggregates/thread/GetThreadById.query.ts
1
import { InvalidState, Query } from '@hicommonwealth/core';
2
import * as schemas from '@hicommonwealth/schemas';
3
import { QueryTypes } from 'sequelize';
4
import { z } from 'zod';
5
import { models, sequelize } from '../../database';
6
import { authOptionalForThread, UnauthorizedView } from '../../middleware';
7
import { filterGates, joinGates, withGates } from '../../utils/gating';
8

9
export function GetThreadById(): Query<typeof schemas.GetThreadById> {
10
  return {
×
11
    ...schemas.GetThreadById,
12
    auth: [authOptionalForThread],
13
    secure: true,
14
    body: async ({ actor, payload, context }) => {
15
      const { thread_id } = payload;
×
16
      const address_id = context?.address?.id;
×
17

18
      const gating = {
×
19
        address_id,
20
        admin_or_moderator:
21
          actor.user.isAdmin ||
×
22
          ['admin', 'moderator'].includes(context?.address?.role || ''),
×
23
      };
24

25
      // find open gates in thread's community
26
      const [open_thread] = await sequelize.query<{
×
27
        id: number;
28
      }>(
29
        `
30
        ${withGates(gating)}
31
        SELECT T.id
32
        FROM
33
          "Threads" T
34
          ${joinGates(gating)}
35
        WHERE
36
          T.id = :thread_id
37
          ${filterGates(gating)}
38
        `,
39
        {
40
          type: QueryTypes.SELECT,
41
          replacements: {
42
            address_id,
43
            community_id: context?.community_id,
44
            thread_id,
45
          },
46
        },
47
      );
48
      if (!open_thread) throw new UnauthorizedView(actor);
×
49

50
      // TODO: refactor using plain sql and add gating filters
51
      const thread = await models.Thread.findOne({
×
52
        where: { id: thread_id },
53
        include: [
54
          {
55
            model: models.Address,
56
            as: 'Address',
57
            include: [
58
              {
59
                model: models.User,
60
                as: 'User',
61
                required: true,
62
                attributes: ['id', 'profile', 'tier'],
63
              },
64
            ],
65
          },
66
          {
67
            model: models.Address,
68
            as: 'collaborators',
69
            include: [
70
              {
71
                model: models.User,
72
                as: 'User',
73
                required: true,
74
                attributes: ['id', 'profile', 'tier'],
75
              },
76
            ],
77
          },
78
          {
79
            model: models.Topic,
80
            as: 'topic',
81
            required: true,
82
          },
83
          {
84
            model: models.Reaction,
85
            as: 'reactions',
86
            include: [
87
              {
88
                model: models.Address,
89
                as: 'Address',
90
                required: true,
91
                include: [
92
                  {
93
                    model: models.User,
94
                    as: 'User',
95
                    required: true,
96
                    attributes: ['id', 'profile', 'tier'],
97
                  },
98
                ],
99
              },
100
            ],
101
          },
102
          {
103
            model: models.ContestAction,
104
            where: { action: 'upvoted' },
105
            required: false,
106
            attributes: ['content_id', 'thread_id'],
107
            include: [
108
              {
109
                model: models.Contest,
110
                on: {
111
                  contest_id: sequelize.where(
112
                    sequelize.col('"ContestActions".contest_id'),
113
                    '=',
114
                    sequelize.col('"ContestActions->Contest".contest_id'),
115
                  ),
116
                  contest_address: sequelize.where(
117
                    sequelize.col('"ContestActions".contest_address'),
118
                    '=',
119
                    sequelize.col('"ContestActions->Contest".contest_address'),
120
                  ),
121
                },
122
                attributes: [
123
                  'contest_id',
124
                  'contest_address',
125
                  'score',
126
                  'start_time',
127
                  'end_time',
128
                ],
129
                include: [
130
                  {
131
                    model: models.ContestManager,
132
                    attributes: ['name', 'cancelled', 'interval'],
133
                  },
134
                ],
135
              },
136
            ],
137
          },
138
          { model: models.ThreadVersionHistory },
139
        ],
140
      });
141

142
      if (!thread) {
×
143
        throw new InvalidState('Thread not found');
×
144
      }
145

146
      return thread!.toJSON() as z.infer<typeof schemas.ThreadView>;
×
147
    },
148
  };
149
}
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