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

hicommonwealth / commonwealth / 16196231406

10 Jul 2025 01:18PM UTC coverage: 40.028% (+0.01%) from 40.016%
16196231406

push

github

web-flow
Merge pull request #12605 from hicommonwealth/ka.deduplicateNames

Added migration to deduplicate names

1860 of 5034 branches covered (36.95%)

Branch coverage included in aggregate %.

3 of 4 new or added lines in 1 file covered. (75.0%)

1 existing line in 1 file now uncovered.

3296 of 7847 relevant lines covered (42.0%)

36.85 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 { Op } from 'sequelize';
5
import { models } from '../../database';
6
import { authVerified } from '../../middleware/auth';
7
import { mustExist } from '../../middleware/guards';
8
import { decodeContent, emitEvent, getDelta, updateTags } from '../../utils';
9

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

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

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

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

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

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

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

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

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

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

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

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

147
        return updated!;
1✔
148
      }
149

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