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

hicommonwealth / commonwealth / 16179408219

09 Jul 2025 08:22PM UTC coverage: 40.028% (+0.01%) from 40.016%
16179408219

Pull #12605

github

web-flow
Merge 9bc0df08d into eb7356236
Pull Request #12605: Added migration to deduplicate names

1860 of 5034 branches covered (36.95%)

Branch coverage included in aggregate %.

4 of 5 new or added lines in 1 file covered. (80.0%)

7 existing lines in 2 files now uncovered.

3296 of 7847 relevant lines covered (42.0%)

36.89 hits per line

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

72.31
/libs/model/src/aggregates/user/UpdateUser.command.ts
1
import { InvalidInput, InvalidState, type Command } from '@hicommonwealth/core';
2
import * as schemas from '@hicommonwealth/schemas';
3
import { DEFAULT_NAME } from '@hicommonwealth/shared';
4
import { models } from '../../database';
5
import { authVerified } from '../../middleware/auth';
6
import { mustExist } from '../../middleware/guards';
7
import { decodeContent, emitEvent, getDelta, updateTags } from '../../utils';
8

9
export function UpdateUser(): Command<typeof schemas.UpdateUser> {
10
  return {
1✔
11
    ...schemas.UpdateUser,
12
    auth: [authVerified()],
13
    body: async ({ actor, payload }) => {
14
      // comparing number to string since command convention requires string id
15
      if (actor.user.id != payload.id)
1!
UNCOV
16
        throw new InvalidInput('Invalid user id');
×
17

18
      const { id, profile, tag_ids } = payload;
1✔
19
      const {
20
        slug,
21
        name,
22
        email,
23
        website,
24
        avatar_url,
25
        socials,
26
        bio: rawBio,
27
        background_image,
28
      } = profile;
1✔
29

30
      const user = (
1✔
31
        await models.User.findOne({
32
          where: { id },
33
          include: {
34
            model: models.ProfileTags,
35
          },
36
        })
37
      )?.toJSON();
38
      mustExist('User', user);
1✔
39

40
      const is_welcome_onboard_flow_complete = !!(
1✔
41
        user.is_welcome_onboard_flow_complete ||
3✔
42
        (name && name !== DEFAULT_NAME)
43
      );
44

45
      const promotional_emails_enabled =
46
        payload.promotional_emails_enabled ??
1✔
47
        user.promotional_emails_enabled ??
48
        false;
49

50
      const notify_user_name_change =
51
        payload.notify_user_name_change ??
1!
52
        user.notify_user_name_change ??
53
        false;
54

55
      const user_delta = getDelta(user, {
1✔
56
        is_welcome_onboard_flow_complete,
57
        promotional_emails_enabled,
58
        notify_user_name_change,
59
        profile: {
60
          email,
61
          slug,
62
          name,
63
          bio: rawBio && decodeContent(rawBio),
1!
64
          website,
65
          avatar_url,
66
          socials,
67
          background_image,
68
        },
69
      });
70

71
      const tags_delta = tag_ids
1!
72
        ? getDelta(
UNCOV
73
            { tag_ids: user.ProfileTags?.map((t) => t.tag_id) },
×
74
            {
75
              tag_ids,
76
            },
77
          )
78
        : {};
79

80
      const update_user = Object.keys(user_delta).length;
1✔
81
      const update_tags = Object.keys(tags_delta).length;
1✔
82

83
      if (update_user || update_tags) {
1!
84
        const updated = await models.sequelize.transaction(
1✔
85
          async (transaction) => {
86
            if (update_tags)
1!
UNCOV
87
              await updateTags(tag_ids!, user.id!, 'user_id', transaction);
×
88

89
            if (update_user) {
1!
90
              // TODO: utility to deep merge deltas
91
              const updates = {
1✔
92
                ...user_delta,
93
                profile: {
94
                  ...user.profile,
95
                  ...user_delta.profile,
96
                  background_image: {
97
                    ...user.profile.background_image,
98
                    ...user_delta.profile?.background_image,
99
                  },
100
                },
101
              };
102
              if (updates.profile.bio === '') {
1!
UNCOV
103
                updates.profile.bio = null;
×
104
              }
105
              const existingUsername = await models.User.findOne({
1✔
106
                where: {
107
                  profile: {
108
                    name: updates?.profile?.name,
109
                  },
110
                },
111
              });
112
              if (existingUsername) {
1!
NEW
113
                throw new InvalidState('Username already exists');
×
114
              }
115
              const [, rows] = await models.User.update(updates, {
1✔
116
                where: { id: user.id },
117
                returning: true,
118
                transaction,
119
              });
120
              const updated_user = rows.at(0);
1✔
121

122
              // emit sign-up flow completed event when:
123
              if (updated_user && user_delta.is_welcome_onboard_flow_complete) {
1!
124
                await emitEvent(
1✔
125
                  models.Outbox,
126
                  [
127
                    {
128
                      event_name: 'SignUpFlowCompleted',
129
                      event_payload: {
130
                        user_id: id,
131
                        address: actor.address!,
132
                        referred_by_address: user.referred_by_address,
133
                        created_at: updated_user.created_at!,
134
                      },
135
                    },
136
                  ],
137
                  transaction,
138
                );
139
              }
140
              return updated_user;
1✔
UNCOV
141
            } else return user;
×
142
          },
143
        );
144

145
        return updated!;
1✔
146
      }
147

148
      // nothing changed
UNCOV
149
      return user;
×
150
    },
151
  };
152
}
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